From a02416ed931a3a10747439c86e16d0e77877da7d Mon Sep 17 00:00:00 2001 From: Emilien Macchi <emilien@redhat.com> Date: Wed, 10 Nov 2021 13:56:55 -0500 Subject: [PATCH 001/360] zuul/networking job: enable Neutron Dynamic Routing service It'll be useful when we'll start testing https://github.com/gophercloud/gophercloud/pull/2241. This patch will install devstack with dynamic routing on the networking job. --- .zuul.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.zuul.yaml b/.zuul.yaml index 1de1c406f2..1f7ed53bc3 100644 --- a/.zuul.yaml +++ b/.zuul.yaml @@ -76,8 +76,10 @@ - acceptance/openstack/networking/v2/extensions/subnetpools - acceptance/openstack/networking/v2/extensions/trunks - acceptance/openstack/networking/v2/extensions/vlantransparent + devstack_projects: 'openstack/neutron-dynamic-routing' devstack_services: - designate + - neutron-dynamic-routing - neutron-ext - octavia From 4063b39a6ac022604b7c8efc64391de503d984fc Mon Sep 17 00:00:00 2001 From: nikParasyr <nik.parasyr@protonmail.com> Date: Thu, 25 Nov 2021 20:38:00 +0100 Subject: [PATCH 002/360] Add Get for blockstorage v3 qos --- .../openstack/blockstorage/v3/qos_test.go | 4 +++ openstack/blockstorage/v3/qos/doc.go | 11 +++++++ openstack/blockstorage/v3/qos/requests.go | 10 ++++++ openstack/blockstorage/v3/qos/results.go | 6 ++++ .../blockstorage/v3/qos/testing/fixtures.go | 31 +++++++++++++++++++ .../v3/qos/testing/requests_test.go | 11 +++++++ openstack/blockstorage/v3/qos/urls.go | 4 +++ 7 files changed, 77 insertions(+) diff --git a/acceptance/openstack/blockstorage/v3/qos_test.go b/acceptance/openstack/blockstorage/v3/qos_test.go index d3883795fb..cd08ee2ce2 100644 --- a/acceptance/openstack/blockstorage/v3/qos_test.go +++ b/acceptance/openstack/blockstorage/v3/qos_test.go @@ -24,6 +24,10 @@ func TestQoS(t *testing.T) { th.AssertNoErr(t, err) defer DeleteQoS(t, client, qos2) + getQoS2, err := qos.Get(client, qos2.ID).Extract() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, qos2, getQoS2) + listOpts := qos.ListOpts{ Limit: 1, } diff --git a/openstack/blockstorage/v3/qos/doc.go b/openstack/blockstorage/v3/qos/doc.go index ba8c8b1935..cda566d7e2 100644 --- a/openstack/blockstorage/v3/qos/doc.go +++ b/openstack/blockstorage/v3/qos/doc.go @@ -50,5 +50,16 @@ Example to list QoS specifications fmt.Printf("List: %+v\n", qos) } +Example to get a single QoS specification + + qosID := "de075d5e-8afc-4e23-9388-b84a5183d1c0" + + singleQos, err := qos.Get(client, test.ID).Extract() + if err != nil { + panic(err) + } + + fmt.Printf("Get: %+v\n", singleQos) + */ package qos diff --git a/openstack/blockstorage/v3/qos/requests.go b/openstack/blockstorage/v3/qos/requests.go index f2d05a3bfd..5b19d64d26 100644 --- a/openstack/blockstorage/v3/qos/requests.go +++ b/openstack/blockstorage/v3/qos/requests.go @@ -143,3 +143,13 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa return QoSPage{pagination.LinkedPageBase{PageResult: r}} }) } + +// Get retrieves details of a single qos. Use Extract to convert its +// result into a QoS. +func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} \ No newline at end of file diff --git a/openstack/blockstorage/v3/qos/results.go b/openstack/blockstorage/v3/qos/results.go index 7d46775805..2967ae1acd 100644 --- a/openstack/blockstorage/v3/qos/results.go +++ b/openstack/blockstorage/v3/qos/results.go @@ -75,3 +75,9 @@ func ExtractQoS(r pagination.Page) ([]QoS, error) { err := (r.(QoSPage)).ExtractInto(&s) return s.QoSs, err } + +// GetResult is the response of a Get operations. Call its Extract method to +// interpret it as a Flavor. +type GetResult struct { + commonResult +} \ No newline at end of file diff --git a/openstack/blockstorage/v3/qos/testing/fixtures.go b/openstack/blockstorage/v3/qos/testing/fixtures.go index 138dc86677..8b677f2150 100644 --- a/openstack/blockstorage/v3/qos/testing/fixtures.go +++ b/openstack/blockstorage/v3/qos/testing/fixtures.go @@ -19,6 +19,15 @@ var createQoSExpected = qos.QoS{ }, } +var getQoSExpected = qos.QoS{ + ID: "d32019d3-bc6e-4319-9c1d-6722fc136a22", + Name: "qos-001", + Consumer: "front-end", + Specs: map[string]string{ + "read_iops_sec": "20000", + }, +} + func MockCreateResponse(t *testing.T) { th.Mux.HandleFunc("/qos-specs", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") @@ -105,3 +114,25 @@ func MockListResponse(t *testing.T) { } }) } + +func MockGetResponse(t *testing.T) { + th.Mux.HandleFunc("/qos-specs/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + w.Header().Add("Content-Type", "application/json") +// w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, ` +{ + "qos_specs": { + "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", + "name": "qos-001", + "consumer": "front-end", + "specs": { + "read_iops_sec": "20000" + } + } +} + `) + }) +} diff --git a/openstack/blockstorage/v3/qos/testing/requests_test.go b/openstack/blockstorage/v3/qos/testing/requests_test.go index e89d9a913e..d5c1d2a126 100644 --- a/openstack/blockstorage/v3/qos/testing/requests_test.go +++ b/openstack/blockstorage/v3/qos/testing/requests_test.go @@ -73,3 +73,14 @@ func TestList(t *testing.T) { t.Errorf("Expected one page, got %d", pages) } } + +func TestGet(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockGetResponse(t) + + actual, err := qos.Get(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, &createQoSExpected, actual) +} \ No newline at end of file diff --git a/openstack/blockstorage/v3/qos/urls.go b/openstack/blockstorage/v3/qos/urls.go index 09787ec29e..e8bbe86e96 100644 --- a/openstack/blockstorage/v3/qos/urls.go +++ b/openstack/blockstorage/v3/qos/urls.go @@ -2,6 +2,10 @@ package qos import "github.com/gophercloud/gophercloud" +func getURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL("qos-specs", id) +} + func createURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("qos-specs") } From eeb33b92b6e877cd24901f9b651c7f1c5f546af7 Mon Sep 17 00:00:00 2001 From: nikParasyr <nik.parasyr@protonmail.com> Date: Thu, 25 Nov 2021 21:40:53 +0100 Subject: [PATCH 003/360] Add Update for blockstorage v3 qos --- .../openstack/blockstorage/v3/qos_test.go | 18 ++++++ openstack/blockstorage/v3/qos/doc.go | 17 ++++++ openstack/blockstorage/v3/qos/requests.go | 60 ++++++++++++++++++- openstack/blockstorage/v3/qos/results.go | 18 +++++- .../blockstorage/v3/qos/testing/fixtures.go | 39 +++++++++++- .../v3/qos/testing/requests_test.go | 23 ++++++- openstack/blockstorage/v3/qos/urls.go | 4 ++ 7 files changed, 173 insertions(+), 6 deletions(-) diff --git a/acceptance/openstack/blockstorage/v3/qos_test.go b/acceptance/openstack/blockstorage/v3/qos_test.go index cd08ee2ce2..3edf3a748b 100644 --- a/acceptance/openstack/blockstorage/v3/qos_test.go +++ b/acceptance/openstack/blockstorage/v3/qos_test.go @@ -28,6 +28,24 @@ func TestQoS(t *testing.T) { th.AssertNoErr(t, err) th.AssertDeepEquals(t, qos2, getQoS2) + updateOpts := qos.UpdateOpts{ + Consumer: qos.ConsumerBack, + Specs: map[string]string{ + "read_iops_sec": "40000", + "write_iops_sec": "40000", + }, + } + + expectedQosSpecs := map[string]string{ + "consumer": "back-end", + "read_iops_sec": "40000", + "write_iops_sec": "40000", + } + + updatedQosSpecs, err := qos.Update(client, qos2.ID, updateOpts).Extract() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, updatedQosSpecs, expectedQosSpecs) + listOpts := qos.ListOpts{ Limit: 1, } diff --git a/openstack/blockstorage/v3/qos/doc.go b/openstack/blockstorage/v3/qos/doc.go index cda566d7e2..3cb58fa689 100644 --- a/openstack/blockstorage/v3/qos/doc.go +++ b/openstack/blockstorage/v3/qos/doc.go @@ -61,5 +61,22 @@ Example to get a single QoS specification fmt.Printf("Get: %+v\n", singleQos) +Example of updating QoSSpec + + qosID := "de075d5e-8afc-4e23-9388-b84a5183d1c0" + + updateOpts := qos.UpdateOpts{ + Consumer: qos.ConsumerBack, + Specs: map[string]string{ + "read_iops_sec": "40000", + }, + } + + specs, err := qos.Update(client, qosID, qosSpecs).Extract() + if err != nil { + panic(err) + } + fmt.Printf("%+v\n", specs) + */ package qos diff --git a/openstack/blockstorage/v3/qos/requests.go b/openstack/blockstorage/v3/qos/requests.go index 5b19d64d26..a0e7faefb7 100644 --- a/openstack/blockstorage/v3/qos/requests.go +++ b/openstack/blockstorage/v3/qos/requests.go @@ -19,7 +19,7 @@ type QoSConsumer string const ( ConsumerFront QoSConsumer = "front-end" - ConsumberBack QoSConsumer = "back-end" + ConsumerBack QoSConsumer = "back-end" ConsumerBoth QoSConsumer = "both" ) @@ -152,4 +152,60 @@ func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return -} \ No newline at end of file +} + +// CreateQosSpecsOptsBuilder allows extensions to add additional parameters to the +// CreateQosSpecs requests. +type CreateQosSpecsOptsBuilder interface { + ToQosSpecsCreateMap() (map[string]interface{}, error) +} + +// UpdateOpts contains options for creating a QoS specification. +// This object is passed to the qos.Update function. +type UpdateOpts struct { + // The consumer of the QoS spec. Possible values are + // both, front-end, back-end. + Consumer QoSConsumer `json:"consumer,omitempty"` + // Specs is a collection of miscellaneous key/values used to set + // specifications for the QoS + Specs map[string]string `json:"-"` +} + +type UpdateOptsBuilder interface { + ToQoSUpdateMap() (map[string]interface{}, error) +} + +// ToQoSUpdateMap assembles a request body based on the contents of a +// UpdateOpts. +func (opts UpdateOpts) ToQoSUpdateMap() (map[string]interface{}, error) { + b, err := gophercloud.BuildRequestBody(opts, "qos_specs") + if err != nil { + return nil, err + } + + if opts.Specs != nil { + if v, ok := b["qos_specs"].(map[string]interface{}); ok { + for key, value := range opts.Specs { + v[key] = value + } + } + } + + return b, nil +} + +// Update will update an existing QoS based on the values in UpdateOpts. +// To extract the QoS object from the response, call the Extract method +// on the UpdateResult. +func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r updateResult) { + b, err := opts.ToQoSUpdateMap() + 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/openstack/blockstorage/v3/qos/results.go b/openstack/blockstorage/v3/qos/results.go index 2967ae1acd..55cefa1ea5 100644 --- a/openstack/blockstorage/v3/qos/results.go +++ b/openstack/blockstorage/v3/qos/results.go @@ -80,4 +80,20 @@ func ExtractQoS(r pagination.Page) ([]QoS, error) { // interpret it as a Flavor. type GetResult struct { commonResult -} \ No newline at end of file +} + +// Extract interprets any updateResult as qosSpecs, if possible. +func (r updateResult) Extract() (map[string]string, error) { + var s struct { + QosSpecs map[string]string `json:"qos_specs"` + } + err := r.ExtractInto(&s) + return s.QosSpecs, err +} + +// updateResult contains the result of a call for (potentially) multiple +// key-value pairs. Call its Extract method to interpret it as a +// map[string]interface. +type updateResult struct { + gophercloud.Result +} diff --git a/openstack/blockstorage/v3/qos/testing/fixtures.go b/openstack/blockstorage/v3/qos/testing/fixtures.go index 8b677f2150..3dc9d9d9d6 100644 --- a/openstack/blockstorage/v3/qos/testing/fixtures.go +++ b/openstack/blockstorage/v3/qos/testing/fixtures.go @@ -120,7 +120,7 @@ func MockGetResponse(t *testing.T) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") -// w.WriteHeader(http.StatusOK) + w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` { @@ -136,3 +136,40 @@ func MockGetResponse(t *testing.T) { `) }) } + +// UpdateBody provides a PUT result of the qos_specs for a QoS +const UpdateBody = ` +{ + "qos_specs" : { + "consumer": "back-end", + "read_iops_sec": "40000", + "write_iops_sec": "40000" + } +} +` + +// UpdateQos is the expected qos_specs returned from PUT on a qos's qos_specs +var UpdateQos = map[string]string{ + "consumer": "back-end", + "read_iops_sec": "40000", + "write_iops_sec": "40000", +} + +func MockUpdateResponse(t *testing.T) { + th.Mux.HandleFunc("/qos-specs/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PUT") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestHeader(t, r, "Accept", "application/json") + th.TestJSONRequest(t, r, `{ + "qos_specs": { + "consumer": "back-end", + "read_iops_sec": "40000", + "write_iops_sec": "40000" + } + }`) + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, UpdateBody) + }) +} diff --git a/openstack/blockstorage/v3/qos/testing/requests_test.go b/openstack/blockstorage/v3/qos/testing/requests_test.go index d5c1d2a126..8cb105cc77 100644 --- a/openstack/blockstorage/v3/qos/testing/requests_test.go +++ b/openstack/blockstorage/v3/qos/testing/requests_test.go @@ -82,5 +82,24 @@ func TestGet(t *testing.T) { actual, err := qos.Get(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract() th.AssertNoErr(t, err) - th.CheckDeepEquals(t, &createQoSExpected, actual) -} \ No newline at end of file + th.CheckDeepEquals(t, &getQoSExpected, actual) +} + +func TestUpdate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + MockUpdateResponse(t) + + updateOpts := qos.UpdateOpts{ + Consumer: qos.ConsumerBack, + Specs: map[string]string{ + "read_iops_sec": "40000", + "write_iops_sec": "40000", + }, + } + + expected := UpdateQos + actual, err := qos.Update(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22", updateOpts).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, expected, actual) +} diff --git a/openstack/blockstorage/v3/qos/urls.go b/openstack/blockstorage/v3/qos/urls.go index e8bbe86e96..ca2ac4f424 100644 --- a/openstack/blockstorage/v3/qos/urls.go +++ b/openstack/blockstorage/v3/qos/urls.go @@ -17,3 +17,7 @@ func listURL(c *gophercloud.ServiceClient) string { func deleteURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("qos-specs", id) } + +func updateURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL("qos-specs", id) +} From 2f8fb3fddeeff336548e63d9fba7bb0d73b0407d Mon Sep 17 00:00:00 2001 From: nikParasyr <nik.parasyr@protonmail.com> Date: Fri, 26 Nov 2021 10:36:44 +0100 Subject: [PATCH 004/360] Add DeleteKeys for blockstorage v3 qos --- .../openstack/blockstorage/v3/qos_test.go | 3 ++ openstack/blockstorage/v3/qos/doc.go | 13 ++++++++- openstack/blockstorage/v3/qos/requests.go | 29 +++++++++++++++++++ .../blockstorage/v3/qos/testing/fixtures.go | 14 +++++++++ .../v3/qos/testing/requests_test.go | 10 +++++++ openstack/blockstorage/v3/qos/urls.go | 4 +++ 6 files changed, 72 insertions(+), 1 deletion(-) diff --git a/acceptance/openstack/blockstorage/v3/qos_test.go b/acceptance/openstack/blockstorage/v3/qos_test.go index 3edf3a748b..deed8b5eb1 100644 --- a/acceptance/openstack/blockstorage/v3/qos_test.go +++ b/acceptance/openstack/blockstorage/v3/qos_test.go @@ -28,6 +28,9 @@ func TestQoS(t *testing.T) { th.AssertNoErr(t, err) th.AssertDeepEquals(t, qos2, getQoS2) + err = qos.DeleteKeys(client, qos2.ID, qos.DeleteKeysOpts{"read_iops_sec"}).ExtractErr() + th.AssertNoErr(t, err) + updateOpts := qos.UpdateOpts{ Consumer: qos.ConsumerBack, Specs: map[string]string{ diff --git a/openstack/blockstorage/v3/qos/doc.go b/openstack/blockstorage/v3/qos/doc.go index 3cb58fa689..0ef15d1026 100644 --- a/openstack/blockstorage/v3/qos/doc.go +++ b/openstack/blockstorage/v3/qos/doc.go @@ -72,11 +72,22 @@ Example of updating QoSSpec }, } - specs, err := qos.Update(client, qosID, qosSpecs).Extract() + specs, err := qos.Update(client, qosID, updateOpts).Extract() if err != nil { panic(err) } fmt.Printf("%+v\n", specs) + +Example of deleting specific keys/specs from a QoS + + qosID := "de075d5e-8afc-4e23-9388-b84a5183d1c0" + + keysToDelete := qos.DeleteKeysOpts{"read_iops_sec"} + err = qos.DeleteKeys(client, qosID, keysToDelete).ExtractErr() + if err != nil { + panic(err) + } + */ package qos diff --git a/openstack/blockstorage/v3/qos/requests.go b/openstack/blockstorage/v3/qos/requests.go index a0e7faefb7..4be588de3e 100644 --- a/openstack/blockstorage/v3/qos/requests.go +++ b/openstack/blockstorage/v3/qos/requests.go @@ -209,3 +209,32 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } + +// DeleteKeysOptsBuilder allows extensions to add additional parameters to the +// CreateExtraSpecs requests. +type DeleteKeysOptsBuilder interface { + ToDeleteKeysCreateMap() (map[string]interface{}, error) +} + +// DeleteKeysOpts is a string slice that contains keys to be deleted. +type DeleteKeysOpts []string + +// ToDeleteKeysCreateMap assembles a body for a Create request based on +// the contents of ExtraSpecsOpts. +func (opts DeleteKeysOpts) ToDeleteKeysCreateMap() (map[string]interface{}, error) { + return map[string]interface{}{"keys": opts}, nil +} + +// DeleteKeys will delete the keys/specs from the specified QoS +func DeleteKeys(client *gophercloud.ServiceClient, qosID string, opts DeleteKeysOptsBuilder) (r DeleteResult) { + b, err := opts.ToDeleteKeysCreateMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Put(deleteKeysURL(client, qosID), b, nil, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} diff --git a/openstack/blockstorage/v3/qos/testing/fixtures.go b/openstack/blockstorage/v3/qos/testing/fixtures.go index 3dc9d9d9d6..be766cbf94 100644 --- a/openstack/blockstorage/v3/qos/testing/fixtures.go +++ b/openstack/blockstorage/v3/qos/testing/fixtures.go @@ -173,3 +173,17 @@ func MockUpdateResponse(t *testing.T) { fmt.Fprintf(w, UpdateBody) }) } + +func MockDeleteKeysResponse(t *testing.T) { + th.Mux.HandleFunc("/qos-specs/d32019d3-bc6e-4319-9c1d-6722fc136a22/delete_keys", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PUT") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestJSONRequest(t, r, `{ + "keys": [ + "read_iops_sec" + ] + }`) + + w.WriteHeader(http.StatusAccepted) + }) +} diff --git a/openstack/blockstorage/v3/qos/testing/requests_test.go b/openstack/blockstorage/v3/qos/testing/requests_test.go index 8cb105cc77..d5f466f59d 100644 --- a/openstack/blockstorage/v3/qos/testing/requests_test.go +++ b/openstack/blockstorage/v3/qos/testing/requests_test.go @@ -103,3 +103,13 @@ func TestUpdate(t *testing.T) { th.AssertNoErr(t, err) th.CheckDeepEquals(t, expected, actual) } + +func TestDeleteKeys(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockDeleteKeysResponse(t) + + res := qos.DeleteKeys(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22", qos.DeleteKeysOpts{"read_iops_sec"}) + th.AssertNoErr(t, res.Err) +} diff --git a/openstack/blockstorage/v3/qos/urls.go b/openstack/blockstorage/v3/qos/urls.go index ca2ac4f424..e8f05d546e 100644 --- a/openstack/blockstorage/v3/qos/urls.go +++ b/openstack/blockstorage/v3/qos/urls.go @@ -21,3 +21,7 @@ func deleteURL(c *gophercloud.ServiceClient, id string) string { func updateURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("qos-specs", id) } + +func deleteKeysURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL("qos-specs", id, "delete_keys") +} From 09cb64dcc7ed2f96db6a94d929faae5338993129 Mon Sep 17 00:00:00 2001 From: nikParasyr <nik.parasyr@protonmail.com> Date: Sat, 27 Nov 2021 19:56:32 +0100 Subject: [PATCH 005/360] Add Associate call to blockstorage/v3/qos --- openstack/blockstorage/v3/qos/doc.go | 14 ++++++++ openstack/blockstorage/v3/qos/requests.go | 35 +++++++++++++++++++ openstack/blockstorage/v3/qos/results.go | 5 +++ .../blockstorage/v3/qos/testing/fixtures.go | 8 +++++ .../v3/qos/testing/requests_test.go | 14 ++++++++ openstack/blockstorage/v3/qos/urls.go | 4 +++ 6 files changed, 80 insertions(+) diff --git a/openstack/blockstorage/v3/qos/doc.go b/openstack/blockstorage/v3/qos/doc.go index 0ef15d1026..4a51324ccc 100644 --- a/openstack/blockstorage/v3/qos/doc.go +++ b/openstack/blockstorage/v3/qos/doc.go @@ -89,5 +89,19 @@ Example of deleting specific keys/specs from a QoS panic(err) } + Example of associating a QoS with a volume type + +qosID := "de075d5e-8afc-4e23-9388-b84a5183d1c0" +volID := "b596be6a-0ce9-43fa-804a-5c5e181ede76" + +associateOpts := qos.AssociateOpts{ + VolumeTypeID: volID, +} + +err = qos.Associate(client, qosID, associateOpts).ExtractErr() +if err != nil { + panic(err) +} + */ package qos diff --git a/openstack/blockstorage/v3/qos/requests.go b/openstack/blockstorage/v3/qos/requests.go index 4be588de3e..2379c5eaf3 100644 --- a/openstack/blockstorage/v3/qos/requests.go +++ b/openstack/blockstorage/v3/qos/requests.go @@ -238,3 +238,38 @@ func DeleteKeys(client *gophercloud.ServiceClient, qosID string, opts DeleteKeys _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } + +// AssociateOpitsBuilder allows extensions to define volume type id +// to the associate query +type AssociateOptsBuilder interface { + ToQosAssociateQuery() (string, error) +} + +// AssociateOpts contains options for associating a QoS with a +// volume type +type AssociateOpts struct { + VolumeTypeID string `q:"vol_type_id" required:"true"` +} + +// ToQosAssociateQuery formats an AssociateOpts into a query string +func (opts AssociateOpts) ToQosAssociateQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// Associate will associate a qos with a volute type +func Associate(client *gophercloud.ServiceClient, qosID string, opts AssociateOptsBuilder) (r AssociateResult) { + url := associateURL(client, qosID) + query, err := opts.ToQosAssociateQuery() + if err != nil { + r.Err = err + return + } + url += query + + resp, err := client.Get(url, nil, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} diff --git a/openstack/blockstorage/v3/qos/results.go b/openstack/blockstorage/v3/qos/results.go index 55cefa1ea5..697df91773 100644 --- a/openstack/blockstorage/v3/qos/results.go +++ b/openstack/blockstorage/v3/qos/results.go @@ -97,3 +97,8 @@ func (r updateResult) Extract() (map[string]string, error) { type updateResult struct { gophercloud.Result } + +// AssociateResult contains the response body and error from a Associate request. +type AssociateResult struct { + gophercloud.ErrResult +} diff --git a/openstack/blockstorage/v3/qos/testing/fixtures.go b/openstack/blockstorage/v3/qos/testing/fixtures.go index be766cbf94..6b5fa6e74d 100644 --- a/openstack/blockstorage/v3/qos/testing/fixtures.go +++ b/openstack/blockstorage/v3/qos/testing/fixtures.go @@ -187,3 +187,11 @@ func MockDeleteKeysResponse(t *testing.T) { w.WriteHeader(http.StatusAccepted) }) } + +func MockAssociateResponse(t *testing.T) { + th.Mux.HandleFunc("/qos-specs/d32019d3-bc6e-4319-9c1d-6722fc136a22/associate", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + w.WriteHeader(http.StatusAccepted) + }) +} diff --git a/openstack/blockstorage/v3/qos/testing/requests_test.go b/openstack/blockstorage/v3/qos/testing/requests_test.go index d5f466f59d..0cf5464a42 100644 --- a/openstack/blockstorage/v3/qos/testing/requests_test.go +++ b/openstack/blockstorage/v3/qos/testing/requests_test.go @@ -113,3 +113,17 @@ func TestDeleteKeys(t *testing.T) { res := qos.DeleteKeys(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22", qos.DeleteKeysOpts{"read_iops_sec"}) th.AssertNoErr(t, res.Err) } + +func TestAssociate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockAssociateResponse(t) + + associateOpts := qos.AssociateOpts{ + VolumeTypeID: "b596be6a-0ce9-43fa-804a-5c5e181ede76", + } + + res := qos.Associate(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22", associateOpts) + th.AssertNoErr(t, res.Err) +} diff --git a/openstack/blockstorage/v3/qos/urls.go b/openstack/blockstorage/v3/qos/urls.go index e8f05d546e..d8dfa0a55e 100644 --- a/openstack/blockstorage/v3/qos/urls.go +++ b/openstack/blockstorage/v3/qos/urls.go @@ -25,3 +25,7 @@ func updateURL(client *gophercloud.ServiceClient, id string) string { func deleteKeysURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("qos-specs", id, "delete_keys") } + +func associateURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL("qos-specs", id, "associate") +} From 56897047bffa211d524d6a4947486f31e3d3de67 Mon Sep 17 00:00:00 2001 From: nikParasyr <nik.parasyr@protonmail.com> Date: Sat, 27 Nov 2021 19:58:33 +0100 Subject: [PATCH 006/360] Add disassociate call to blockstorage/v3/qos --- openstack/blockstorage/v3/qos/doc.go | 15 ++++++++ openstack/blockstorage/v3/qos/requests.go | 35 +++++++++++++++++++ openstack/blockstorage/v3/qos/results.go | 5 +++ .../blockstorage/v3/qos/testing/fixtures.go | 8 +++++ .../v3/qos/testing/requests_test.go | 14 ++++++++ openstack/blockstorage/v3/qos/urls.go | 4 +++ 6 files changed, 81 insertions(+) diff --git a/openstack/blockstorage/v3/qos/doc.go b/openstack/blockstorage/v3/qos/doc.go index 4a51324ccc..7a50c61713 100644 --- a/openstack/blockstorage/v3/qos/doc.go +++ b/openstack/blockstorage/v3/qos/doc.go @@ -103,5 +103,20 @@ if err != nil { panic(err) } +Example of disassociating a QoS from a volume type + +qosID := "de075d5e-8afc-4e23-9388-b84a5183d1c0" +volID := "b596be6a-0ce9-43fa-804a-5c5e181ede76" + +disassociateOpts := qos.DisassociateOpts{ + VolumeTypeID: volID, +} + +err = qos.Disassociate(client, qosID, disassociateOpts).ExtractErr() +if err != nil { + panic(err) +} + + */ package qos diff --git a/openstack/blockstorage/v3/qos/requests.go b/openstack/blockstorage/v3/qos/requests.go index 2379c5eaf3..dc16801b32 100644 --- a/openstack/blockstorage/v3/qos/requests.go +++ b/openstack/blockstorage/v3/qos/requests.go @@ -273,3 +273,38 @@ func Associate(client *gophercloud.ServiceClient, qosID string, opts AssociateOp _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } + +// DisassociateOpitsBuilder allows extensions to define volume type id +// to the disassociate query +type DisassociateOptsBuilder interface { + ToQosDisassociateQuery() (string, error) +} + +// DisassociateOpts contains options for disassociating a QoS from a +// volume type +type DisassociateOpts struct { + VolumeTypeID string `q:"vol_type_id" required:"true"` +} + +// ToQosDisassociateQuery formats a DisassociateOpts into a query string +func (opts DisassociateOpts) ToQosDisassociateQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// Disassociate will disassociate a qos from a volute type +func Disassociate(client *gophercloud.ServiceClient, qosID string, opts DisassociateOptsBuilder) (r DisassociateResult) { + url := disassociateURL(client, qosID) + query, err := opts.ToQosDisassociateQuery() + if err != nil { + r.Err = err + return + } + url += query + + resp, err := client.Get(url, nil, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} diff --git a/openstack/blockstorage/v3/qos/results.go b/openstack/blockstorage/v3/qos/results.go index 697df91773..68a6999e57 100644 --- a/openstack/blockstorage/v3/qos/results.go +++ b/openstack/blockstorage/v3/qos/results.go @@ -102,3 +102,8 @@ type updateResult struct { type AssociateResult struct { gophercloud.ErrResult } + +// DisassociateResult contains the response body and error from a Disassociate request. +type DisassociateResult struct { + gophercloud.ErrResult +} diff --git a/openstack/blockstorage/v3/qos/testing/fixtures.go b/openstack/blockstorage/v3/qos/testing/fixtures.go index 6b5fa6e74d..c655f8b008 100644 --- a/openstack/blockstorage/v3/qos/testing/fixtures.go +++ b/openstack/blockstorage/v3/qos/testing/fixtures.go @@ -195,3 +195,11 @@ func MockAssociateResponse(t *testing.T) { w.WriteHeader(http.StatusAccepted) }) } + +func MockDisassociateResponse(t *testing.T) { + th.Mux.HandleFunc("/qos-specs/d32019d3-bc6e-4319-9c1d-6722fc136a22/disassociate", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + w.WriteHeader(http.StatusAccepted) + }) +} diff --git a/openstack/blockstorage/v3/qos/testing/requests_test.go b/openstack/blockstorage/v3/qos/testing/requests_test.go index 0cf5464a42..0e9dcee2ff 100644 --- a/openstack/blockstorage/v3/qos/testing/requests_test.go +++ b/openstack/blockstorage/v3/qos/testing/requests_test.go @@ -127,3 +127,17 @@ func TestAssociate(t *testing.T) { res := qos.Associate(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22", associateOpts) th.AssertNoErr(t, res.Err) } + +func TestDisssociate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockDisassociateResponse(t) + + disassociateOpts := qos.DisassociateOpts{ + VolumeTypeID: "b596be6a-0ce9-43fa-804a-5c5e181ede76", + } + + res := qos.Disassociate(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22", disassociateOpts) + th.AssertNoErr(t, res.Err) +} diff --git a/openstack/blockstorage/v3/qos/urls.go b/openstack/blockstorage/v3/qos/urls.go index d8dfa0a55e..b2a7d774db 100644 --- a/openstack/blockstorage/v3/qos/urls.go +++ b/openstack/blockstorage/v3/qos/urls.go @@ -29,3 +29,7 @@ func deleteKeysURL(client *gophercloud.ServiceClient, id string) string { func associateURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("qos-specs", id, "associate") } + +func disassociateURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL("qos-specs", id, "disassociate") +} From 129400fff9810fede558ed7d0b3a5c1732e42b04 Mon Sep 17 00:00:00 2001 From: nikParasyr <nik.parasyr@protonmail.com> Date: Sat, 27 Nov 2021 20:00:23 +0100 Subject: [PATCH 007/360] Add disassociateAll call to blockstorage/v3/qos --- openstack/blockstorage/v3/qos/doc.go | 9 +++++++++ openstack/blockstorage/v3/qos/requests.go | 9 +++++++++ openstack/blockstorage/v3/qos/results.go | 5 +++++ openstack/blockstorage/v3/qos/testing/fixtures.go | 8 ++++++++ openstack/blockstorage/v3/qos/testing/requests_test.go | 10 ++++++++++ openstack/blockstorage/v3/qos/urls.go | 4 ++++ 6 files changed, 45 insertions(+) diff --git a/openstack/blockstorage/v3/qos/doc.go b/openstack/blockstorage/v3/qos/doc.go index 7a50c61713..594ba2fea8 100644 --- a/openstack/blockstorage/v3/qos/doc.go +++ b/openstack/blockstorage/v3/qos/doc.go @@ -117,6 +117,15 @@ if err != nil { panic(err) } +Example of disaassociating a Qos from all volume types + +qosID := "de075d5e-8afc-4e23-9388-b84a5183d1c0" + +err = qos.DisassociateAll(client, qosID).ExtractErr() +if err != nil { + panic(err) +} + */ package qos diff --git a/openstack/blockstorage/v3/qos/requests.go b/openstack/blockstorage/v3/qos/requests.go index dc16801b32..7c91311ddd 100644 --- a/openstack/blockstorage/v3/qos/requests.go +++ b/openstack/blockstorage/v3/qos/requests.go @@ -308,3 +308,12 @@ func Disassociate(client *gophercloud.ServiceClient, qosID string, opts Disassoc _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } + +// DisassociateAll will disassociate a qos from all volute types +func DisassociateAll(client *gophercloud.ServiceClient, qosID string) (r DisassociateAllResult) { + resp, err := client.Get(disassociateAllURL(client, qosID), nil, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} diff --git a/openstack/blockstorage/v3/qos/results.go b/openstack/blockstorage/v3/qos/results.go index 68a6999e57..1f07c591d4 100644 --- a/openstack/blockstorage/v3/qos/results.go +++ b/openstack/blockstorage/v3/qos/results.go @@ -107,3 +107,8 @@ type AssociateResult struct { type DisassociateResult struct { gophercloud.ErrResult } + +// DisassociateAllResult contains the response body and error from a DisassociateAll request. +type DisassociateAllResult struct { + gophercloud.ErrResult +} diff --git a/openstack/blockstorage/v3/qos/testing/fixtures.go b/openstack/blockstorage/v3/qos/testing/fixtures.go index c655f8b008..20ed9c44a7 100644 --- a/openstack/blockstorage/v3/qos/testing/fixtures.go +++ b/openstack/blockstorage/v3/qos/testing/fixtures.go @@ -203,3 +203,11 @@ func MockDisassociateResponse(t *testing.T) { w.WriteHeader(http.StatusAccepted) }) } + +func MockDisassociateAllResponse(t *testing.T) { + th.Mux.HandleFunc("/qos-specs/d32019d3-bc6e-4319-9c1d-6722fc136a22/disassociate_all", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + w.WriteHeader(http.StatusAccepted) + }) +} diff --git a/openstack/blockstorage/v3/qos/testing/requests_test.go b/openstack/blockstorage/v3/qos/testing/requests_test.go index 0e9dcee2ff..72d4a011ac 100644 --- a/openstack/blockstorage/v3/qos/testing/requests_test.go +++ b/openstack/blockstorage/v3/qos/testing/requests_test.go @@ -141,3 +141,13 @@ func TestDisssociate(t *testing.T) { res := qos.Disassociate(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22", disassociateOpts) th.AssertNoErr(t, res.Err) } + +func TestDissasociateAll(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockDisassociateAllResponse(t) + + res := qos.DisassociateAll(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22") + th.AssertNoErr(t, res.Err) +} diff --git a/openstack/blockstorage/v3/qos/urls.go b/openstack/blockstorage/v3/qos/urls.go index b2a7d774db..54d8ffe116 100644 --- a/openstack/blockstorage/v3/qos/urls.go +++ b/openstack/blockstorage/v3/qos/urls.go @@ -33,3 +33,7 @@ func associateURL(client *gophercloud.ServiceClient, id string) string { func disassociateURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("qos-specs", id, "disassociate") } + +func disassociateAllURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL("qos-specs", id, "disassociate_all") +} \ No newline at end of file From 78b19bebebacb80bd6277cc30eca2dd7e21069fb Mon Sep 17 00:00:00 2001 From: nikParasyr <nik.parasyr@protonmail.com> Date: Sat, 27 Nov 2021 20:21:09 +0100 Subject: [PATCH 008/360] Add ListAssociations to blockstorage/v3/qos --- .../openstack/blockstorage/v3/qos_test.go | 54 +++++++++++++++++++ openstack/blockstorage/v3/qos/doc.go | 17 ++++++ openstack/blockstorage/v3/qos/requests.go | 9 ++++ openstack/blockstorage/v3/qos/results.go | 30 +++++++++++ .../blockstorage/v3/qos/testing/fixtures.go | 19 +++++++ .../v3/qos/testing/requests_test.go | 25 +++++++++ openstack/blockstorage/v3/qos/urls.go | 6 ++- 7 files changed, 159 insertions(+), 1 deletion(-) diff --git a/acceptance/openstack/blockstorage/v3/qos_test.go b/acceptance/openstack/blockstorage/v3/qos_test.go index deed8b5eb1..831ae53a1c 100644 --- a/acceptance/openstack/blockstorage/v3/qos_test.go +++ b/acceptance/openstack/blockstorage/v3/qos_test.go @@ -4,6 +4,7 @@ import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/qos" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" @@ -73,3 +74,56 @@ func TestQoS(t *testing.T) { th.AssertNoErr(t, err) } + +func TestQoSAssociations(t *testing.T) { + clients.SkipRelease(t, "stable/mitaka") + clients.RequireAdmin(t) + + client, err := clients.NewBlockStorageV3Client() + th.AssertNoErr(t, err) + + qos1, err := CreateQoS(t, client) + th.AssertNoErr(t, err) + defer DeleteQoS(t, client, qos1) + + vt, err := CreateVolumeType(t, client) + th.AssertNoErr(t, err) + defer DeleteVolumeType(t, client, vt) + + associateOpts := qos.AssociateOpts{ + VolumeTypeID: vt.ID, + } + + err = qos.Associate(client, qos1.ID, associateOpts).ExtractErr() + th.AssertNoErr(t, err) + + allQosAssociations, err := qos.ListAssociations(client, qos1.ID).AllPages() + th.AssertNoErr(t, err) + + allAssociations, err := qos.ExtractAssociations(allQosAssociations) + th.AssertNoErr(t, err) + tools.PrintResource(t, allAssociations) + th.AssertEquals(t, 1, len(allAssociations)) + th.AssertEquals(t, vt.ID, allAssociations[0].ID) + + disassociateOpts := qos.DisassociateOpts{ + VolumeTypeID: vt.ID, + } + + err = qos.Disassociate(client, qos1.ID, disassociateOpts).ExtractErr() + th.AssertNoErr(t, err) + + allQosAssociations, err = qos.ListAssociations(client, qos1.ID).AllPages() + th.AssertNoErr(t, err) + + allAssociations, err = qos.ExtractAssociations(allQosAssociations) + th.AssertNoErr(t, err) + tools.PrintResource(t, allAssociations) + th.AssertEquals(t, 0, len(allAssociations)) + + err = qos.Associate(client, qos1.ID, associateOpts).ExtractErr() + th.AssertNoErr(t, err) + + err = qos.DisassociateAll(client, qos1.ID).ExtractErr() + th.AssertNoErr(t, err) +} diff --git a/openstack/blockstorage/v3/qos/doc.go b/openstack/blockstorage/v3/qos/doc.go index 594ba2fea8..4d86b0ceea 100644 --- a/openstack/blockstorage/v3/qos/doc.go +++ b/openstack/blockstorage/v3/qos/doc.go @@ -126,6 +126,23 @@ if err != nil { panic(err) } +Example of listing all associations of a QoS + +qosID := "de075d5e-8afc-4e23-9388-b84a5183d1c0" + +allQosAssociations, err := qos.ListAssociations(client, qosID).AllPages() +if err != nil { + panic(err) +} + +allAssociations, err := qos.ExtractAssociations(allQosAssociations) +if err != nil { + panic(err) +} + +for _, association := range allAssociations { + fmt.Printf("Association: %+v\n", association) +} */ package qos diff --git a/openstack/blockstorage/v3/qos/requests.go b/openstack/blockstorage/v3/qos/requests.go index 7c91311ddd..8169f82190 100644 --- a/openstack/blockstorage/v3/qos/requests.go +++ b/openstack/blockstorage/v3/qos/requests.go @@ -317,3 +317,12 @@ func DisassociateAll(client *gophercloud.ServiceClient, qosID string) (r Disasso _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } + +// ListAssociations retrieves the associations of a QoS. +func ListAssociations(client *gophercloud.ServiceClient, qosID string) pagination.Pager { + url := listAssociationsURL(client, qosID) + + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return AssociationPage{pagination.SinglePageBase(r)} + }) +} diff --git a/openstack/blockstorage/v3/qos/results.go b/openstack/blockstorage/v3/qos/results.go index 1f07c591d4..34008aed80 100644 --- a/openstack/blockstorage/v3/qos/results.go +++ b/openstack/blockstorage/v3/qos/results.go @@ -112,3 +112,33 @@ type DisassociateResult struct { type DisassociateAllResult struct { gophercloud.ErrResult } + +// QoS contains all the information associated with an OpenStack QoS specification. +type QosAssociation struct { + // Name is the name of the associated resource + Name string `json:"name"` + // Unique identifier of the associated resources + ID string `json:"id"` + // AssociationType of the QoS Association + AssociationType string `json:"association_type"` +} + +// AssociationPage contains a single page of all Associations of a QoS +type AssociationPage struct { + pagination.SinglePageBase +} + +// IsEmpty indicates whether an Association page is empty. +func (page AssociationPage) IsEmpty() (bool, error) { + v, err := ExtractAssociations(page) + return len(v) == 0, err +} + +// ExtractAssociations interprets a page of results as a slice of QosAssociations +func ExtractAssociations(r pagination.Page) ([]QosAssociation, error) { + var s struct { + QosAssociations []QosAssociation `json:"qos_associations"` + } + err := (r.(AssociationPage)).ExtractInto(&s) + return s.QosAssociations, err +} diff --git a/openstack/blockstorage/v3/qos/testing/fixtures.go b/openstack/blockstorage/v3/qos/testing/fixtures.go index 20ed9c44a7..d3bf00d69f 100644 --- a/openstack/blockstorage/v3/qos/testing/fixtures.go +++ b/openstack/blockstorage/v3/qos/testing/fixtures.go @@ -211,3 +211,22 @@ func MockDisassociateAllResponse(t *testing.T) { w.WriteHeader(http.StatusAccepted) }) } + +func MockListAssociationsResponse(t *testing.T) { + th.Mux.HandleFunc("/qos-specs/d32019d3-bc6e-4319-9c1d-6722fc136a22/associations", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + w.Header().Add("Content-Type", "application/json") + fmt.Fprintf(w, ` + { + "qos_associations": [ + { + "name": "foo", + "id": "2f954bcf047c4ee9b09a37d49ae6db54", + "association_type": "volume_type" + } + ] + } + `) + }) +} diff --git a/openstack/blockstorage/v3/qos/testing/requests_test.go b/openstack/blockstorage/v3/qos/testing/requests_test.go index 72d4a011ac..e452fa2eee 100644 --- a/openstack/blockstorage/v3/qos/testing/requests_test.go +++ b/openstack/blockstorage/v3/qos/testing/requests_test.go @@ -151,3 +151,28 @@ func TestDissasociateAll(t *testing.T) { res := qos.DisassociateAll(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22") th.AssertNoErr(t, res.Err) } + +func TestQosAssociationsList(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockListAssociationsResponse(t) + + expected := []qos.QosAssociation{ + { + Name: "foo", + ID: "2f954bcf047c4ee9b09a37d49ae6db54", + AssociationType: "volume_type", + }, + } + + allPages, err := qos.ListAssociations(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").AllPages() + th.AssertNoErr(t, err) + + actual, err := qos.ExtractAssociations(allPages) + th.AssertNoErr(t, err) + + if !reflect.DeepEqual(expected, actual) { + t.Errorf("Expected %#v, but was %#v", expected, actual) + } +} diff --git a/openstack/blockstorage/v3/qos/urls.go b/openstack/blockstorage/v3/qos/urls.go index 54d8ffe116..e0e4a0eec8 100644 --- a/openstack/blockstorage/v3/qos/urls.go +++ b/openstack/blockstorage/v3/qos/urls.go @@ -36,4 +36,8 @@ func disassociateURL(client *gophercloud.ServiceClient, id string) string { func disassociateAllURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("qos-specs", id, "disassociate_all") -} \ No newline at end of file +} + +func listAssociationsURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL("qos-specs", id, "associations") +} From 19071a7894fa90c219fcd6764cc11f00198dc07e Mon Sep 17 00:00:00 2001 From: nikParasyr <nik.parasyr@protonmail.com> Date: Tue, 7 Dec 2021 13:53:36 +0100 Subject: [PATCH 009/360] Fix identation on blockstorage/v3/qos/docs.go --- openstack/blockstorage/v3/qos/doc.go | 72 ++++++++++++++-------------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/openstack/blockstorage/v3/qos/doc.go b/openstack/blockstorage/v3/qos/doc.go index 4d86b0ceea..0a6008159a 100644 --- a/openstack/blockstorage/v3/qos/doc.go +++ b/openstack/blockstorage/v3/qos/doc.go @@ -89,60 +89,60 @@ Example of deleting specific keys/specs from a QoS panic(err) } - Example of associating a QoS with a volume type +Example of associating a QoS with a volume type -qosID := "de075d5e-8afc-4e23-9388-b84a5183d1c0" -volID := "b596be6a-0ce9-43fa-804a-5c5e181ede76" + qosID := "de075d5e-8afc-4e23-9388-b84a5183d1c0" + volID := "b596be6a-0ce9-43fa-804a-5c5e181ede76" -associateOpts := qos.AssociateOpts{ - VolumeTypeID: volID, -} + associateOpts := qos.AssociateOpts{ + VolumeTypeID: volID, + } -err = qos.Associate(client, qosID, associateOpts).ExtractErr() -if err != nil { - panic(err) -} + err = qos.Associate(client, qosID, associateOpts).ExtractErr() + if err != nil { + panic(err) + } Example of disassociating a QoS from a volume type -qosID := "de075d5e-8afc-4e23-9388-b84a5183d1c0" -volID := "b596be6a-0ce9-43fa-804a-5c5e181ede76" + qosID := "de075d5e-8afc-4e23-9388-b84a5183d1c0" + volID := "b596be6a-0ce9-43fa-804a-5c5e181ede76" -disassociateOpts := qos.DisassociateOpts{ - VolumeTypeID: volID, -} + disassociateOpts := qos.DisassociateOpts{ + VolumeTypeID: volID, + } -err = qos.Disassociate(client, qosID, disassociateOpts).ExtractErr() -if err != nil { - panic(err) -} + err = qos.Disassociate(client, qosID, disassociateOpts).ExtractErr() + if err != nil { + panic(err) + } Example of disaassociating a Qos from all volume types -qosID := "de075d5e-8afc-4e23-9388-b84a5183d1c0" + qosID := "de075d5e-8afc-4e23-9388-b84a5183d1c0" -err = qos.DisassociateAll(client, qosID).ExtractErr() -if err != nil { - panic(err) -} + err = qos.DisassociateAll(client, qosID).ExtractErr() + if err != nil { + panic(err) + } Example of listing all associations of a QoS -qosID := "de075d5e-8afc-4e23-9388-b84a5183d1c0" + qosID := "de075d5e-8afc-4e23-9388-b84a5183d1c0" -allQosAssociations, err := qos.ListAssociations(client, qosID).AllPages() -if err != nil { - panic(err) -} + allQosAssociations, err := qos.ListAssociations(client, qosID).AllPages() + if err != nil { + panic(err) + } -allAssociations, err := qos.ExtractAssociations(allQosAssociations) -if err != nil { - panic(err) -} + allAssociations, err := qos.ExtractAssociations(allQosAssociations) + if err != nil { + panic(err) + } -for _, association := range allAssociations { - fmt.Printf("Association: %+v\n", association) -} + for _, association := range allAssociations { + fmt.Printf("Association: %+v\n", association) + } */ package qos From b9e592632c484c107d0060e9fcf31f6fc837ad3a Mon Sep 17 00:00:00 2001 From: Pierre Prinetti <pierreprinetti@redhat.com> Date: Wed, 8 Dec 2021 13:57:48 +0100 Subject: [PATCH 010/360] Bump golang.org/x/crypto --- go.mod | 3 +-- go.sum | 17 ++++++++--------- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/go.mod b/go.mod index 94cd805878..c6baab410b 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,6 @@ module github.com/gophercloud/gophercloud go 1.13 require ( - golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad - golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 // indirect + golang.org/x/crypto v0.0.0-20211202192323-5770296d904e gopkg.in/yaml.v2 v2.4.0 ) diff --git a/go.sum b/go.sum index 552ef90a11..dec4af3cdc 100644 --- a/go.sum +++ b/go.sum @@ -1,15 +1,14 @@ -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad h1:DN0cp81fZ3njFcrLCytUHRSUkqBjfTo4Tx9RJTWs0EY= -golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw= +golang.org/x/crypto v0.0.0-20211202192323-5770296d904e h1:MUP6MR3rJ7Gk9LEia0LP2ytiH6MuCfs7qYz+47jGdD8= +golang.org/x/crypto v0.0.0-20211202192323-5770296d904e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= From e951a1adb02d93c7e3b7d963da1ac876638d1ae7 Mon Sep 17 00:00:00 2001 From: Pierre Prinetti <pierreprinetti@redhat.com> Date: Fri, 10 Dec 2021 15:28:16 +0100 Subject: [PATCH 011/360] test: Run against supported Go versions Run against Go tip and against the oldest supported version. --- .github/workflows/unit.yml | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/.github/workflows/unit.yml b/.github/workflows/unit.yml index f61ff79d30..9f4e925d44 100644 --- a/.github/workflows/unit.yml +++ b/.github/workflows/unit.yml @@ -7,7 +7,8 @@ jobs: fail-fast: false matrix: go-version: - - "1.15" + - "1.13" + - "1" env: GO111MODULE: "on" @@ -24,7 +25,6 @@ jobs: run: | go get golang.org/x/crypto/ssh go get github.com/wadey/gocovmerge - go get github.com/mattn/goveralls go get golang.org/x/tools/cmd/goimports - name: Run go vet @@ -40,3 +40,13 @@ jobs: - uses: shogo82148/actions-goveralls@v1 with: path-to-profile: cover.out + flag-name: Go-${{ matrix.go-version }} + parallel: true + + finish: + needs: test + runs-on: ubuntu-latest + steps: + - uses: shogo82148/actions-goveralls@v1 + with: + parallel-finished: true From c19d1713377bcd981338aa897fcbcc837819f02e Mon Sep 17 00:00:00 2001 From: Pierre Prinetti <pierreprinetti@redhat.com> Date: Fri, 10 Dec 2021 15:45:11 +0100 Subject: [PATCH 012/360] test: unpin Ubuntu, use latest --- .github/workflows/unit.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/unit.yml b/.github/workflows/unit.yml index 9f4e925d44..79bc9a0c9e 100644 --- a/.github/workflows/unit.yml +++ b/.github/workflows/unit.yml @@ -2,7 +2,7 @@ on: [push, pull_request] name: Unit Testing jobs: test: - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest strategy: fail-fast: false matrix: From b54053acaa7efa38fbb4fd83ae709f293c255fc5 Mon Sep 17 00:00:00 2001 From: Pierre Prinetti <pierreprinetti@redhat.com> Date: Fri, 10 Dec 2021 15:46:24 +0100 Subject: [PATCH 013/360] test: Let Go install code dependencies --- .github/workflows/unit.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/unit.yml b/.github/workflows/unit.yml index 79bc9a0c9e..22adf27ace 100644 --- a/.github/workflows/unit.yml +++ b/.github/workflows/unit.yml @@ -23,7 +23,6 @@ jobs: - name: Setup environment run: | - go get golang.org/x/crypto/ssh go get github.com/wadey/gocovmerge go get golang.org/x/tools/cmd/goimports From 4c88bfb378db9c8fa50311cc8e34f18807a8b84b Mon Sep 17 00:00:00 2001 From: Pierre Prinetti <pierreprinetti@redhat.com> Date: Fri, 10 Dec 2021 15:46:55 +0100 Subject: [PATCH 014/360] test: Run reauth tests separately Running retests in a separate action to increase explicitness and to parallelise WRT to the other unit tests. --- .github/workflows/reauth-retests.yaml | 22 ++++++++++++++++++++++ .github/workflows/unit.yml | 1 - 2 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/reauth-retests.yaml diff --git a/.github/workflows/reauth-retests.yaml b/.github/workflows/reauth-retests.yaml new file mode 100644 index 0000000000..bc4a928d9a --- /dev/null +++ b/.github/workflows/reauth-retests.yaml @@ -0,0 +1,22 @@ +on: [push, pull_request] +name: Reauth retests +jobs: + test: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + go-version: + - "1" + + steps: + - name: Setup Go ${{ matrix.go-version }} + uses: actions/setup-go@v2 + with: + go-version: ${{ matrix.go-version }} + + - uses: actions/checkout@v2 + + - name: Run reauth retests + run: | + ./script/unittest diff --git a/.github/workflows/unit.yml b/.github/workflows/unit.yml index 22adf27ace..c937a18729 100644 --- a/.github/workflows/unit.yml +++ b/.github/workflows/unit.yml @@ -33,7 +33,6 @@ jobs: - name: Run unit tests run: | ./script/coverage - ./script/unittest ./script/format - uses: shogo82148/actions-goveralls@v1 From e2ccc7b4a6ef7ddfaed974284c97361b638e55da Mon Sep 17 00:00:00 2001 From: Pierre Prinetti <pierreprinetti@redhat.com> Date: Fri, 10 Dec 2021 15:55:08 +0100 Subject: [PATCH 015/360] Set Go minimum version to 1.14 The `testhelper` package uses `http.Header`'s method `Values`, which [was introduced in GO 1.14][1]. [1]: https://pkg.go.dev/net/http#Header.Values --- .github/workflows/unit.yml | 2 +- go.mod | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/unit.yml b/.github/workflows/unit.yml index c937a18729..c17e7eec5c 100644 --- a/.github/workflows/unit.yml +++ b/.github/workflows/unit.yml @@ -7,7 +7,7 @@ jobs: fail-fast: false matrix: go-version: - - "1.13" + - "1.14" - "1" env: diff --git a/go.mod b/go.mod index c6baab410b..c51d7daaaf 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/gophercloud/gophercloud -go 1.13 +go 1.14 require ( golang.org/x/crypto v0.0.0-20211202192323-5770296d904e From 89cbf2a90f4123e6738c7d99fac2825c98e72797 Mon Sep 17 00:00:00 2001 From: Pierre Prinetti <pierreprinetti@redhat.com> Date: Fri, 10 Dec 2021 15:07:34 +0100 Subject: [PATCH 016/360] actions: add a go mod tidy check Errors if dependencies were modified and `go mod tidy` was not run before push. --- .github/workflows/gomod.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 .github/workflows/gomod.yml diff --git a/.github/workflows/gomod.yml b/.github/workflows/gomod.yml new file mode 100644 index 0000000000..155e8f7ea2 --- /dev/null +++ b/.github/workflows/gomod.yml @@ -0,0 +1,11 @@ +on: [push, pull_request] +name: go mod +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-go@v2 + with: + go-version: '1' + - run: if [ $(go mod tidy && git diff | wc -l) -gt 0 ]; then git diff && exit 1; fi From cfaf0b765ccfeb04e1363ee2ec55c4ce24a1adb1 Mon Sep 17 00:00:00 2001 From: Pierre Prinetti <pierreprinetti@redhat.com> Date: Fri, 10 Dec 2021 16:09:43 +0100 Subject: [PATCH 017/360] test: Run go fmt ./... The syntax for build hints has slightly changed and gofmt complains. --- acceptance/openstack/baremetal/httpbasic/allocations_test.go | 1 + acceptance/openstack/baremetal/httpbasic/ports_test.go | 1 + acceptance/openstack/baremetal/noauth/allocations_test.go | 1 + acceptance/openstack/baremetal/noauth/ports_test.go | 1 + acceptance/openstack/baremetal/v1/allocations_test.go | 1 + acceptance/openstack/baremetal/v1/nodes_test.go | 1 + acceptance/openstack/baremetal/v1/ports_test.go | 1 + acceptance/openstack/blockstorage/apiversions_test.go | 1 + acceptance/openstack/blockstorage/extensions/backups_test.go | 1 + .../openstack/blockstorage/extensions/schedulerhints_test.go | 1 + .../openstack/blockstorage/extensions/schedulerstats_test.go | 1 + acceptance/openstack/blockstorage/extensions/services_test.go | 1 + .../openstack/blockstorage/extensions/volumeactions_test.go | 1 + .../openstack/blockstorage/extensions/volumetenants_test.go | 1 + acceptance/openstack/blockstorage/noauth/snapshots_test.go | 1 + acceptance/openstack/blockstorage/noauth/volumes_test.go | 1 + acceptance/openstack/blockstorage/v1/snapshots_test.go | 1 + acceptance/openstack/blockstorage/v1/volumes_test.go | 1 + acceptance/openstack/blockstorage/v1/volumetypes_test.go | 1 + acceptance/openstack/blockstorage/v2/snapshots_test.go | 1 + acceptance/openstack/blockstorage/v2/volumes_test.go | 1 + acceptance/openstack/blockstorage/v3/quotaset_test.go | 1 + acceptance/openstack/blockstorage/v3/snapshots_test.go | 1 + acceptance/openstack/blockstorage/v3/volumeattachments_test.go | 1 + acceptance/openstack/blockstorage/v3/volumes_test.go | 1 + acceptance/openstack/blockstorage/v3/volumetypes_test.go | 1 + acceptance/openstack/client_test.go | 1 + acceptance/openstack/clustering/v1/actions_test.go | 1 + acceptance/openstack/clustering/v1/clusters_test.go | 1 + acceptance/openstack/clustering/v1/events_test.go | 1 + acceptance/openstack/clustering/v1/nodes_test.go | 1 + acceptance/openstack/clustering/v1/policies_test.go | 1 + acceptance/openstack/clustering/v1/policytypes_test.go | 1 + acceptance/openstack/clustering/v1/profiles_test.go | 1 + acceptance/openstack/clustering/v1/profiletypes_test.go | 1 + acceptance/openstack/clustering/v1/receivers_test.go | 1 + acceptance/openstack/clustering/v1/webhooktrigger_test.go | 1 + acceptance/openstack/compute/v2/aggregates_test.go | 1 + acceptance/openstack/compute/v2/attachinterfaces_test.go | 1 + acceptance/openstack/compute/v2/availabilityzones_test.go | 1 + acceptance/openstack/compute/v2/bootfromvolume_test.go | 1 + acceptance/openstack/compute/v2/defsecrules_test.go | 1 + acceptance/openstack/compute/v2/diagnostics_test.go | 1 + acceptance/openstack/compute/v2/extension_test.go | 1 + acceptance/openstack/compute/v2/flavors_test.go | 1 + acceptance/openstack/compute/v2/floatingip_test.go | 1 + acceptance/openstack/compute/v2/hypervisors_test.go | 1 + acceptance/openstack/compute/v2/images_test.go | 1 + acceptance/openstack/compute/v2/instance_actions_test.go | 1 + acceptance/openstack/compute/v2/keypairs_test.go | 1 + acceptance/openstack/compute/v2/limits_test.go | 1 + acceptance/openstack/compute/v2/migrate_test.go | 1 + acceptance/openstack/compute/v2/network_test.go | 1 + acceptance/openstack/compute/v2/quotaset_test.go | 1 + acceptance/openstack/compute/v2/remoteconsoles_test.go | 1 + acceptance/openstack/compute/v2/rescueunrescue_test.go | 1 + acceptance/openstack/compute/v2/secgroup_test.go | 1 + acceptance/openstack/compute/v2/servergroup_test.go | 1 + acceptance/openstack/compute/v2/servers_test.go | 1 + acceptance/openstack/compute/v2/services_test.go | 1 + acceptance/openstack/compute/v2/tenantnetworks_test.go | 1 + acceptance/openstack/compute/v2/usage_test.go | 1 + acceptance/openstack/compute/v2/volumeattach_test.go | 1 + acceptance/openstack/container/v1/capsules_test.go | 1 + acceptance/openstack/containerinfra/v1/certificates_test.go | 1 + acceptance/openstack/containerinfra/v1/clusters_test.go | 1 + acceptance/openstack/containerinfra/v1/clustertemplates_test.go | 1 + acceptance/openstack/containerinfra/v1/nodegroups_test.go | 1 + acceptance/openstack/containerinfra/v1/quotas_test.go | 1 + acceptance/openstack/db/v1/configurations_test.go | 1 + acceptance/openstack/db/v1/databases_test.go | 1 + acceptance/openstack/db/v1/flavors_test.go | 1 + acceptance/openstack/db/v1/instances_test.go | 1 + acceptance/openstack/db/v1/users_test.go | 1 + acceptance/openstack/dns/v2/recordsets_test.go | 1 + acceptance/openstack/dns/v2/transfers_test.go | 1 + acceptance/openstack/dns/v2/zones_test.go | 1 + acceptance/openstack/identity/v2/extension_test.go | 1 + acceptance/openstack/identity/v2/role_test.go | 1 + acceptance/openstack/identity/v2/tenant_test.go | 1 + acceptance/openstack/identity/v2/token_test.go | 1 + acceptance/openstack/identity/v2/user_test.go | 1 + acceptance/openstack/identity/v3/applicationcredentials_test.go | 1 + acceptance/openstack/identity/v3/credentials_test.go | 1 + acceptance/openstack/identity/v3/domains_test.go | 1 + acceptance/openstack/identity/v3/ec2credentials_test.go | 1 + acceptance/openstack/identity/v3/endpoint_test.go | 1 + acceptance/openstack/identity/v3/groups_test.go | 1 + acceptance/openstack/identity/v3/oauth1_test.go | 1 + acceptance/openstack/identity/v3/policies_test.go | 1 + acceptance/openstack/identity/v3/projects_test.go | 1 + acceptance/openstack/identity/v3/reauth_test.go | 1 + acceptance/openstack/identity/v3/regions_test.go | 1 + acceptance/openstack/identity/v3/roles_test.go | 1 + acceptance/openstack/identity/v3/service_test.go | 1 + acceptance/openstack/identity/v3/token_test.go | 1 + acceptance/openstack/identity/v3/trusts_test.go | 1 + acceptance/openstack/identity/v3/users_test.go | 1 + acceptance/openstack/imageservice/v2/imageimport_test.go | 1 + acceptance/openstack/imageservice/v2/images_test.go | 1 + acceptance/openstack/imageservice/v2/tasks_test.go | 1 + acceptance/openstack/keymanager/v1/acls_test.go | 1 + acceptance/openstack/keymanager/v1/containers_test.go | 1 + acceptance/openstack/keymanager/v1/orders_test.go | 1 + acceptance/openstack/keymanager/v1/secrets_test.go | 1 + acceptance/openstack/loadbalancer/v2/amphorae_test.go | 1 + acceptance/openstack/loadbalancer/v2/l7policies_test.go | 1 + acceptance/openstack/loadbalancer/v2/listeners_test.go | 1 + acceptance/openstack/loadbalancer/v2/loadbalancers_test.go | 1 + acceptance/openstack/loadbalancer/v2/monitors_test.go | 1 + acceptance/openstack/loadbalancer/v2/pools_test.go | 1 + acceptance/openstack/loadbalancer/v2/providers_test.go | 1 + acceptance/openstack/loadbalancer/v2/quotas_test.go | 1 + acceptance/openstack/messaging/v2/claims_test.go | 1 + acceptance/openstack/messaging/v2/message_test.go | 1 + acceptance/openstack/messaging/v2/queue_test.go | 1 + acceptance/openstack/networking/v2/apiversion_test.go | 1 + acceptance/openstack/networking/v2/extension_test.go | 1 + .../openstack/networking/v2/extensions/agents/agents_test.go | 1 + .../openstack/networking/v2/extensions/attributestags_test.go | 1 + acceptance/openstack/networking/v2/extensions/dns/dns_test.go | 1 + .../openstack/networking/v2/extensions/fwaas/firewall_test.go | 1 + .../openstack/networking/v2/extensions/fwaas/policy_test.go | 1 + acceptance/openstack/networking/v2/extensions/fwaas/rule_test.go | 1 + .../openstack/networking/v2/extensions/fwaas_v2/groups_test.go | 1 + .../openstack/networking/v2/extensions/fwaas_v2/policy_test.go | 1 + .../openstack/networking/v2/extensions/fwaas_v2/rule_test.go | 1 + .../networking/v2/extensions/layer3/addressscopes_test.go | 1 + .../networking/v2/extensions/layer3/floatingips_test.go | 1 + .../openstack/networking/v2/extensions/layer3/routers_test.go | 1 + .../openstack/networking/v2/extensions/lbaas/members_test.go | 1 + .../openstack/networking/v2/extensions/lbaas/monitors_test.go | 1 + .../openstack/networking/v2/extensions/lbaas/pools_test.go | 1 + acceptance/openstack/networking/v2/extensions/lbaas/vips_test.go | 1 + .../networking/v2/extensions/lbaas_v2/l7policies_test.go | 1 + .../networking/v2/extensions/lbaas_v2/listeners_test.go | 1 + .../networking/v2/extensions/lbaas_v2/loadbalancers_test.go | 1 + .../openstack/networking/v2/extensions/lbaas_v2/monitors_test.go | 1 + .../openstack/networking/v2/extensions/lbaas_v2/pools_test.go | 1 + acceptance/openstack/networking/v2/extensions/mtu/mtu_test.go | 1 + .../networkipavailabilities/networkipavailabilities_test.go | 1 + .../networking/v2/extensions/portsbinding/portsbinding_test.go | 1 + acceptance/openstack/networking/v2/extensions/provider_test.go | 1 + .../networking/v2/extensions/qos/policies/policies_test.go | 1 + .../openstack/networking/v2/extensions/quotas/quotas_test.go | 1 + .../networking/v2/extensions/rbacpolicies/rbacpolicies_test.go | 1 + acceptance/openstack/networking/v2/extensions/security_test.go | 1 + .../networking/v2/extensions/subnetpools/subnetpools_test.go | 1 + .../openstack/networking/v2/extensions/trunks/trunks_test.go | 1 + .../v2/extensions/vlantransparent/vlantransparent_test.go | 1 + .../openstack/networking/v2/extensions/vpnaas/group_test.go | 1 + .../openstack/networking/v2/extensions/vpnaas/ikepolicy_test.go | 1 + .../networking/v2/extensions/vpnaas/ipsecpolicy_test.go | 1 + .../openstack/networking/v2/extensions/vpnaas/service_test.go | 1 + .../networking/v2/extensions/vpnaas/siteconnection_test.go | 1 + acceptance/openstack/networking/v2/networks_test.go | 1 + acceptance/openstack/networking/v2/ports_test.go | 1 + acceptance/openstack/networking/v2/subnets_test.go | 1 + acceptance/openstack/objectstorage/v1/accounts_test.go | 1 + acceptance/openstack/objectstorage/v1/containers_test.go | 1 + acceptance/openstack/objectstorage/v1/objects_test.go | 1 + acceptance/openstack/orchestration/v1/buildinfo_test.go | 1 + acceptance/openstack/orchestration/v1/stackevents_test.go | 1 + acceptance/openstack/orchestration/v1/stackresources_test.go | 1 + acceptance/openstack/orchestration/v1/stacks_test.go | 1 + acceptance/openstack/orchestration/v1/stacktemplates_test.go | 1 + acceptance/openstack/pkg.go | 1 + .../openstack/sharedfilesystems/v2/availabilityzones_test.go | 1 + .../openstack/sharedfilesystems/v2/securityservices_test.go | 1 + acceptance/openstack/sharedfilesystems/v2/sharenetworks_test.go | 1 + acceptance/openstack/sharedfilesystems/v2/shares_test.go | 1 + acceptance/openstack/sharedfilesystems/v2/sharetypes_test.go | 1 + acceptance/openstack/sharedfilesystems/v2/snapshots_test.go | 1 + 173 files changed, 173 insertions(+) diff --git a/acceptance/openstack/baremetal/httpbasic/allocations_test.go b/acceptance/openstack/baremetal/httpbasic/allocations_test.go index 7af0b5700b..afe44a0cf2 100644 --- a/acceptance/openstack/baremetal/httpbasic/allocations_test.go +++ b/acceptance/openstack/baremetal/httpbasic/allocations_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || baremetal || allocations // +build acceptance baremetal allocations package httpbasic diff --git a/acceptance/openstack/baremetal/httpbasic/ports_test.go b/acceptance/openstack/baremetal/httpbasic/ports_test.go index 8c54a549bd..ebed5b6785 100644 --- a/acceptance/openstack/baremetal/httpbasic/ports_test.go +++ b/acceptance/openstack/baremetal/httpbasic/ports_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || baremetal || ports // +build acceptance baremetal ports package httpbasic diff --git a/acceptance/openstack/baremetal/noauth/allocations_test.go b/acceptance/openstack/baremetal/noauth/allocations_test.go index dc66a107bd..825fd4d93e 100644 --- a/acceptance/openstack/baremetal/noauth/allocations_test.go +++ b/acceptance/openstack/baremetal/noauth/allocations_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || baremetal || allocations // +build acceptance baremetal allocations package noauth diff --git a/acceptance/openstack/baremetal/noauth/ports_test.go b/acceptance/openstack/baremetal/noauth/ports_test.go index 2498cdb68f..a45b26d462 100644 --- a/acceptance/openstack/baremetal/noauth/ports_test.go +++ b/acceptance/openstack/baremetal/noauth/ports_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || baremetal || ports // +build acceptance baremetal ports package noauth diff --git a/acceptance/openstack/baremetal/v1/allocations_test.go b/acceptance/openstack/baremetal/v1/allocations_test.go index e342dc1fe6..7c6bfac52f 100644 --- a/acceptance/openstack/baremetal/v1/allocations_test.go +++ b/acceptance/openstack/baremetal/v1/allocations_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || baremetal || allocations // +build acceptance baremetal allocations package v1 diff --git a/acceptance/openstack/baremetal/v1/nodes_test.go b/acceptance/openstack/baremetal/v1/nodes_test.go index f34015563d..68e8b79fac 100644 --- a/acceptance/openstack/baremetal/v1/nodes_test.go +++ b/acceptance/openstack/baremetal/v1/nodes_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || baremetal || nodes // +build acceptance baremetal nodes package v1 diff --git a/acceptance/openstack/baremetal/v1/ports_test.go b/acceptance/openstack/baremetal/v1/ports_test.go index b938c193c3..e263402c29 100644 --- a/acceptance/openstack/baremetal/v1/ports_test.go +++ b/acceptance/openstack/baremetal/v1/ports_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || baremetal || ports // +build acceptance baremetal ports package v1 diff --git a/acceptance/openstack/blockstorage/apiversions_test.go b/acceptance/openstack/blockstorage/apiversions_test.go index 5c94d55928..77ccda0f31 100644 --- a/acceptance/openstack/blockstorage/apiversions_test.go +++ b/acceptance/openstack/blockstorage/apiversions_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || blockstorage // +build acceptance blockstorage package blockstorage diff --git a/acceptance/openstack/blockstorage/extensions/backups_test.go b/acceptance/openstack/blockstorage/extensions/backups_test.go index aa5e6c1b09..4b3a4ff60e 100644 --- a/acceptance/openstack/blockstorage/extensions/backups_test.go +++ b/acceptance/openstack/blockstorage/extensions/backups_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || blockstorage // +build acceptance blockstorage package extensions diff --git a/acceptance/openstack/blockstorage/extensions/schedulerhints_test.go b/acceptance/openstack/blockstorage/extensions/schedulerhints_test.go index f2623fca41..f86713d384 100644 --- a/acceptance/openstack/blockstorage/extensions/schedulerhints_test.go +++ b/acceptance/openstack/blockstorage/extensions/schedulerhints_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || blockstorage // +build acceptance blockstorage package extensions diff --git a/acceptance/openstack/blockstorage/extensions/schedulerstats_test.go b/acceptance/openstack/blockstorage/extensions/schedulerstats_test.go index 5b4c35e2d3..7e4f01ccde 100644 --- a/acceptance/openstack/blockstorage/extensions/schedulerstats_test.go +++ b/acceptance/openstack/blockstorage/extensions/schedulerstats_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || blockstorage // +build acceptance blockstorage package extensions diff --git a/acceptance/openstack/blockstorage/extensions/services_test.go b/acceptance/openstack/blockstorage/extensions/services_test.go index 47043e51c7..301155bcfd 100644 --- a/acceptance/openstack/blockstorage/extensions/services_test.go +++ b/acceptance/openstack/blockstorage/extensions/services_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || blockstorage // +build acceptance blockstorage package extensions diff --git a/acceptance/openstack/blockstorage/extensions/volumeactions_test.go b/acceptance/openstack/blockstorage/extensions/volumeactions_test.go index e5cc069ebc..f0f1365df0 100644 --- a/acceptance/openstack/blockstorage/extensions/volumeactions_test.go +++ b/acceptance/openstack/blockstorage/extensions/volumeactions_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || blockstorage // +build acceptance blockstorage package extensions diff --git a/acceptance/openstack/blockstorage/extensions/volumetenants_test.go b/acceptance/openstack/blockstorage/extensions/volumetenants_test.go index 4de21abe4a..8b4e2cbf71 100644 --- a/acceptance/openstack/blockstorage/extensions/volumetenants_test.go +++ b/acceptance/openstack/blockstorage/extensions/volumetenants_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || blockstorage // +build acceptance blockstorage package extensions diff --git a/acceptance/openstack/blockstorage/noauth/snapshots_test.go b/acceptance/openstack/blockstorage/noauth/snapshots_test.go index f287c3e5f2..6d16e1a23c 100644 --- a/acceptance/openstack/blockstorage/noauth/snapshots_test.go +++ b/acceptance/openstack/blockstorage/noauth/snapshots_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || blockstorage // +build acceptance blockstorage package noauth diff --git a/acceptance/openstack/blockstorage/noauth/volumes_test.go b/acceptance/openstack/blockstorage/noauth/volumes_test.go index 4e10344cf6..47891719d4 100644 --- a/acceptance/openstack/blockstorage/noauth/volumes_test.go +++ b/acceptance/openstack/blockstorage/noauth/volumes_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || blockstorage // +build acceptance blockstorage package noauth diff --git a/acceptance/openstack/blockstorage/v1/snapshots_test.go b/acceptance/openstack/blockstorage/v1/snapshots_test.go index 354537187a..ff0a2d07e3 100644 --- a/acceptance/openstack/blockstorage/v1/snapshots_test.go +++ b/acceptance/openstack/blockstorage/v1/snapshots_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || blockstorage // +build acceptance blockstorage package v1 diff --git a/acceptance/openstack/blockstorage/v1/volumes_test.go b/acceptance/openstack/blockstorage/v1/volumes_test.go index bdbadf1d56..98204724bb 100644 --- a/acceptance/openstack/blockstorage/v1/volumes_test.go +++ b/acceptance/openstack/blockstorage/v1/volumes_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || blockstorage // +build acceptance blockstorage package v1 diff --git a/acceptance/openstack/blockstorage/v1/volumetypes_test.go b/acceptance/openstack/blockstorage/v1/volumetypes_test.go index ace09bc4d0..390df09587 100644 --- a/acceptance/openstack/blockstorage/v1/volumetypes_test.go +++ b/acceptance/openstack/blockstorage/v1/volumetypes_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || blockstorage // +build acceptance blockstorage package v1 diff --git a/acceptance/openstack/blockstorage/v2/snapshots_test.go b/acceptance/openstack/blockstorage/v2/snapshots_test.go index a24f4dceff..0c5df1d446 100644 --- a/acceptance/openstack/blockstorage/v2/snapshots_test.go +++ b/acceptance/openstack/blockstorage/v2/snapshots_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || blockstorage // +build acceptance blockstorage package v2 diff --git a/acceptance/openstack/blockstorage/v2/volumes_test.go b/acceptance/openstack/blockstorage/v2/volumes_test.go index 4399a7a790..569918e5ca 100644 --- a/acceptance/openstack/blockstorage/v2/volumes_test.go +++ b/acceptance/openstack/blockstorage/v2/volumes_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || blockstorage // +build acceptance blockstorage package v2 diff --git a/acceptance/openstack/blockstorage/v3/quotaset_test.go b/acceptance/openstack/blockstorage/v3/quotaset_test.go index 46b30b2671..70b365262f 100644 --- a/acceptance/openstack/blockstorage/v3/quotaset_test.go +++ b/acceptance/openstack/blockstorage/v3/quotaset_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || quotasets // +build acceptance quotasets package v3 diff --git a/acceptance/openstack/blockstorage/v3/snapshots_test.go b/acceptance/openstack/blockstorage/v3/snapshots_test.go index a7b52c44a2..748f9ee4ef 100644 --- a/acceptance/openstack/blockstorage/v3/snapshots_test.go +++ b/acceptance/openstack/blockstorage/v3/snapshots_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || blockstorage // +build acceptance blockstorage package v3 diff --git a/acceptance/openstack/blockstorage/v3/volumeattachments_test.go b/acceptance/openstack/blockstorage/v3/volumeattachments_test.go index 26921a0525..4b6963998d 100644 --- a/acceptance/openstack/blockstorage/v3/volumeattachments_test.go +++ b/acceptance/openstack/blockstorage/v3/volumeattachments_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || blockstorage // +build acceptance blockstorage package v3 diff --git a/acceptance/openstack/blockstorage/v3/volumes_test.go b/acceptance/openstack/blockstorage/v3/volumes_test.go index 3164c1b37c..f47545b702 100644 --- a/acceptance/openstack/blockstorage/v3/volumes_test.go +++ b/acceptance/openstack/blockstorage/v3/volumes_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || blockstorage // +build acceptance blockstorage package v3 diff --git a/acceptance/openstack/blockstorage/v3/volumetypes_test.go b/acceptance/openstack/blockstorage/v3/volumetypes_test.go index e6e898df40..2029deb0ba 100644 --- a/acceptance/openstack/blockstorage/v3/volumetypes_test.go +++ b/acceptance/openstack/blockstorage/v3/volumetypes_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || blockstorage // +build acceptance blockstorage package v3 diff --git a/acceptance/openstack/client_test.go b/acceptance/openstack/client_test.go index b48492398f..d497c969a9 100644 --- a/acceptance/openstack/client_test.go +++ b/acceptance/openstack/client_test.go @@ -1,3 +1,4 @@ +//go:build acceptance // +build acceptance package openstack diff --git a/acceptance/openstack/clustering/v1/actions_test.go b/acceptance/openstack/clustering/v1/actions_test.go index eca8fc62c6..f8a7843eb1 100644 --- a/acceptance/openstack/clustering/v1/actions_test.go +++ b/acceptance/openstack/clustering/v1/actions_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || clustering || actions // +build acceptance clustering actions package v1 diff --git a/acceptance/openstack/clustering/v1/clusters_test.go b/acceptance/openstack/clustering/v1/clusters_test.go index bad20dfc05..7c6f80666f 100644 --- a/acceptance/openstack/clustering/v1/clusters_test.go +++ b/acceptance/openstack/clustering/v1/clusters_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || clustering || policies // +build acceptance clustering policies package v1 diff --git a/acceptance/openstack/clustering/v1/events_test.go b/acceptance/openstack/clustering/v1/events_test.go index 447fecd54e..6e8b50a827 100644 --- a/acceptance/openstack/clustering/v1/events_test.go +++ b/acceptance/openstack/clustering/v1/events_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || clustering || events // +build acceptance clustering events package v1 diff --git a/acceptance/openstack/clustering/v1/nodes_test.go b/acceptance/openstack/clustering/v1/nodes_test.go index 58fcbf33b0..c41e34567a 100644 --- a/acceptance/openstack/clustering/v1/nodes_test.go +++ b/acceptance/openstack/clustering/v1/nodes_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || clustering || policies // +build acceptance clustering policies package v1 diff --git a/acceptance/openstack/clustering/v1/policies_test.go b/acceptance/openstack/clustering/v1/policies_test.go index 3d58351567..a1fc2be6ef 100644 --- a/acceptance/openstack/clustering/v1/policies_test.go +++ b/acceptance/openstack/clustering/v1/policies_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || clustering || policies // +build acceptance clustering policies package v1 diff --git a/acceptance/openstack/clustering/v1/policytypes_test.go b/acceptance/openstack/clustering/v1/policytypes_test.go index fdb42a3153..70a43f9c66 100644 --- a/acceptance/openstack/clustering/v1/policytypes_test.go +++ b/acceptance/openstack/clustering/v1/policytypes_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || clustering || policytypes // +build acceptance clustering policytypes package v1 diff --git a/acceptance/openstack/clustering/v1/profiles_test.go b/acceptance/openstack/clustering/v1/profiles_test.go index 9a7986b8bc..b65b7d4565 100644 --- a/acceptance/openstack/clustering/v1/profiles_test.go +++ b/acceptance/openstack/clustering/v1/profiles_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || clustering || policies // +build acceptance clustering policies package v1 diff --git a/acceptance/openstack/clustering/v1/profiletypes_test.go b/acceptance/openstack/clustering/v1/profiletypes_test.go index 039f926f5a..9a7c700252 100644 --- a/acceptance/openstack/clustering/v1/profiletypes_test.go +++ b/acceptance/openstack/clustering/v1/profiletypes_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || clustering || profiletypes // +build acceptance clustering profiletypes package v1 diff --git a/acceptance/openstack/clustering/v1/receivers_test.go b/acceptance/openstack/clustering/v1/receivers_test.go index fbf5565c93..56b862abbf 100644 --- a/acceptance/openstack/clustering/v1/receivers_test.go +++ b/acceptance/openstack/clustering/v1/receivers_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || clustering || policies // +build acceptance clustering policies package v1 diff --git a/acceptance/openstack/clustering/v1/webhooktrigger_test.go b/acceptance/openstack/clustering/v1/webhooktrigger_test.go index 627e8c02cc..b4b4a2e37e 100644 --- a/acceptance/openstack/clustering/v1/webhooktrigger_test.go +++ b/acceptance/openstack/clustering/v1/webhooktrigger_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || clustering || webhooks // +build acceptance clustering webhooks package v1 diff --git a/acceptance/openstack/compute/v2/aggregates_test.go b/acceptance/openstack/compute/v2/aggregates_test.go index 543d958992..208ad3b1ad 100644 --- a/acceptance/openstack/compute/v2/aggregates_test.go +++ b/acceptance/openstack/compute/v2/aggregates_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || compute || aggregates // +build acceptance compute aggregates package v2 diff --git a/acceptance/openstack/compute/v2/attachinterfaces_test.go b/acceptance/openstack/compute/v2/attachinterfaces_test.go index b8f9680285..3efef0c1d5 100644 --- a/acceptance/openstack/compute/v2/attachinterfaces_test.go +++ b/acceptance/openstack/compute/v2/attachinterfaces_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || compute || servers // +build acceptance compute servers package v2 diff --git a/acceptance/openstack/compute/v2/availabilityzones_test.go b/acceptance/openstack/compute/v2/availabilityzones_test.go index 4d030c2968..967d56f5ab 100644 --- a/acceptance/openstack/compute/v2/availabilityzones_test.go +++ b/acceptance/openstack/compute/v2/availabilityzones_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || compute || availabilityzones // +build acceptance compute availabilityzones package v2 diff --git a/acceptance/openstack/compute/v2/bootfromvolume_test.go b/acceptance/openstack/compute/v2/bootfromvolume_test.go index f98c561425..9980660463 100644 --- a/acceptance/openstack/compute/v2/bootfromvolume_test.go +++ b/acceptance/openstack/compute/v2/bootfromvolume_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || compute || bootfromvolume // +build acceptance compute bootfromvolume package v2 diff --git a/acceptance/openstack/compute/v2/defsecrules_test.go b/acceptance/openstack/compute/v2/defsecrules_test.go index e97a378718..cb0352f80d 100644 --- a/acceptance/openstack/compute/v2/defsecrules_test.go +++ b/acceptance/openstack/compute/v2/defsecrules_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || compute || defsecrules // +build acceptance compute defsecrules package v2 diff --git a/acceptance/openstack/compute/v2/diagnostics_test.go b/acceptance/openstack/compute/v2/diagnostics_test.go index 43b0ee9a5a..a19d54fe66 100644 --- a/acceptance/openstack/compute/v2/diagnostics_test.go +++ b/acceptance/openstack/compute/v2/diagnostics_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || compute || limits // +build acceptance compute limits package v2 diff --git a/acceptance/openstack/compute/v2/extension_test.go b/acceptance/openstack/compute/v2/extension_test.go index f76cc52e06..8fb0f28ba1 100644 --- a/acceptance/openstack/compute/v2/extension_test.go +++ b/acceptance/openstack/compute/v2/extension_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || compute || extensions // +build acceptance compute extensions package v2 diff --git a/acceptance/openstack/compute/v2/flavors_test.go b/acceptance/openstack/compute/v2/flavors_test.go index 3972b17bfa..c58f99452e 100644 --- a/acceptance/openstack/compute/v2/flavors_test.go +++ b/acceptance/openstack/compute/v2/flavors_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || compute || flavors // +build acceptance compute flavors package v2 diff --git a/acceptance/openstack/compute/v2/floatingip_test.go b/acceptance/openstack/compute/v2/floatingip_test.go index 8130873676..dae68e7b63 100644 --- a/acceptance/openstack/compute/v2/floatingip_test.go +++ b/acceptance/openstack/compute/v2/floatingip_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || compute || servers // +build acceptance compute servers package v2 diff --git a/acceptance/openstack/compute/v2/hypervisors_test.go b/acceptance/openstack/compute/v2/hypervisors_test.go index 1a4e53ebd6..ee4410726f 100644 --- a/acceptance/openstack/compute/v2/hypervisors_test.go +++ b/acceptance/openstack/compute/v2/hypervisors_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || compute || hypervisors // +build acceptance compute hypervisors package v2 diff --git a/acceptance/openstack/compute/v2/images_test.go b/acceptance/openstack/compute/v2/images_test.go index d7fe19b35b..4d25d29416 100644 --- a/acceptance/openstack/compute/v2/images_test.go +++ b/acceptance/openstack/compute/v2/images_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || compute || images // +build acceptance compute images package v2 diff --git a/acceptance/openstack/compute/v2/instance_actions_test.go b/acceptance/openstack/compute/v2/instance_actions_test.go index a63f45766d..e2f861ad91 100644 --- a/acceptance/openstack/compute/v2/instance_actions_test.go +++ b/acceptance/openstack/compute/v2/instance_actions_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || compute || limits // +build acceptance compute limits package v2 diff --git a/acceptance/openstack/compute/v2/keypairs_test.go b/acceptance/openstack/compute/v2/keypairs_test.go index 723bae2fe5..f057191633 100644 --- a/acceptance/openstack/compute/v2/keypairs_test.go +++ b/acceptance/openstack/compute/v2/keypairs_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || compute || keypairs // +build acceptance compute keypairs package v2 diff --git a/acceptance/openstack/compute/v2/limits_test.go b/acceptance/openstack/compute/v2/limits_test.go index 8133999c6c..3aab23c3a7 100644 --- a/acceptance/openstack/compute/v2/limits_test.go +++ b/acceptance/openstack/compute/v2/limits_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || compute || limits // +build acceptance compute limits package v2 diff --git a/acceptance/openstack/compute/v2/migrate_test.go b/acceptance/openstack/compute/v2/migrate_test.go index 0661d12dce..a5b99ee318 100644 --- a/acceptance/openstack/compute/v2/migrate_test.go +++ b/acceptance/openstack/compute/v2/migrate_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || compute || servers // +build acceptance compute servers package v2 diff --git a/acceptance/openstack/compute/v2/network_test.go b/acceptance/openstack/compute/v2/network_test.go index cb5d396c9c..345356830f 100644 --- a/acceptance/openstack/compute/v2/network_test.go +++ b/acceptance/openstack/compute/v2/network_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || compute || servers // +build acceptance compute servers package v2 diff --git a/acceptance/openstack/compute/v2/quotaset_test.go b/acceptance/openstack/compute/v2/quotaset_test.go index 9beb785654..1341207fa7 100644 --- a/acceptance/openstack/compute/v2/quotaset_test.go +++ b/acceptance/openstack/compute/v2/quotaset_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || compute || quotasets // +build acceptance compute quotasets package v2 diff --git a/acceptance/openstack/compute/v2/remoteconsoles_test.go b/acceptance/openstack/compute/v2/remoteconsoles_test.go index 9dd5863c9c..1a32de0045 100644 --- a/acceptance/openstack/compute/v2/remoteconsoles_test.go +++ b/acceptance/openstack/compute/v2/remoteconsoles_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || compute || remoteconsoles // +build acceptance compute remoteconsoles package v2 diff --git a/acceptance/openstack/compute/v2/rescueunrescue_test.go b/acceptance/openstack/compute/v2/rescueunrescue_test.go index bbc38fafa8..b4304bfdfc 100644 --- a/acceptance/openstack/compute/v2/rescueunrescue_test.go +++ b/acceptance/openstack/compute/v2/rescueunrescue_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || compute || rescueunrescue // +build acceptance compute rescueunrescue package v2 diff --git a/acceptance/openstack/compute/v2/secgroup_test.go b/acceptance/openstack/compute/v2/secgroup_test.go index 4404665711..174c3f418f 100644 --- a/acceptance/openstack/compute/v2/secgroup_test.go +++ b/acceptance/openstack/compute/v2/secgroup_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || compute || secgroups // +build acceptance compute secgroups package v2 diff --git a/acceptance/openstack/compute/v2/servergroup_test.go b/acceptance/openstack/compute/v2/servergroup_test.go index f8fe03d20e..ee9b89271b 100644 --- a/acceptance/openstack/compute/v2/servergroup_test.go +++ b/acceptance/openstack/compute/v2/servergroup_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || compute || servergroups // +build acceptance compute servergroups package v2 diff --git a/acceptance/openstack/compute/v2/servers_test.go b/acceptance/openstack/compute/v2/servers_test.go index dd36253d3a..d65b69654d 100644 --- a/acceptance/openstack/compute/v2/servers_test.go +++ b/acceptance/openstack/compute/v2/servers_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || compute || servers // +build acceptance compute servers package v2 diff --git a/acceptance/openstack/compute/v2/services_test.go b/acceptance/openstack/compute/v2/services_test.go index 4223332aa4..6dc590eeee 100644 --- a/acceptance/openstack/compute/v2/services_test.go +++ b/acceptance/openstack/compute/v2/services_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || compute || services // +build acceptance compute services package v2 diff --git a/acceptance/openstack/compute/v2/tenantnetworks_test.go b/acceptance/openstack/compute/v2/tenantnetworks_test.go index a53c64d353..f404663472 100644 --- a/acceptance/openstack/compute/v2/tenantnetworks_test.go +++ b/acceptance/openstack/compute/v2/tenantnetworks_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || compute || servers // +build acceptance compute servers package v2 diff --git a/acceptance/openstack/compute/v2/usage_test.go b/acceptance/openstack/compute/v2/usage_test.go index c998b59e2f..9f73eb9e85 100644 --- a/acceptance/openstack/compute/v2/usage_test.go +++ b/acceptance/openstack/compute/v2/usage_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || compute || usage // +build acceptance compute usage package v2 diff --git a/acceptance/openstack/compute/v2/volumeattach_test.go b/acceptance/openstack/compute/v2/volumeattach_test.go index d4b841f233..1d3dac08f8 100644 --- a/acceptance/openstack/compute/v2/volumeattach_test.go +++ b/acceptance/openstack/compute/v2/volumeattach_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || compute || volumeattach // +build acceptance compute volumeattach package v2 diff --git a/acceptance/openstack/container/v1/capsules_test.go b/acceptance/openstack/container/v1/capsules_test.go index 5ea7f5dbda..451d5e2853 100644 --- a/acceptance/openstack/container/v1/capsules_test.go +++ b/acceptance/openstack/container/v1/capsules_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || containers || capsules // +build acceptance containers capsules package v1 diff --git a/acceptance/openstack/containerinfra/v1/certificates_test.go b/acceptance/openstack/containerinfra/v1/certificates_test.go index c3860f9a71..e0e14a9702 100644 --- a/acceptance/openstack/containerinfra/v1/certificates_test.go +++ b/acceptance/openstack/containerinfra/v1/certificates_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || containerinfra // +build acceptance containerinfra package v1 diff --git a/acceptance/openstack/containerinfra/v1/clusters_test.go b/acceptance/openstack/containerinfra/v1/clusters_test.go index 2e23afae0a..5bc4c6aecd 100644 --- a/acceptance/openstack/containerinfra/v1/clusters_test.go +++ b/acceptance/openstack/containerinfra/v1/clusters_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || containerinfra // +build acceptance containerinfra package v1 diff --git a/acceptance/openstack/containerinfra/v1/clustertemplates_test.go b/acceptance/openstack/containerinfra/v1/clustertemplates_test.go index fb27d0971f..b50edffe8a 100644 --- a/acceptance/openstack/containerinfra/v1/clustertemplates_test.go +++ b/acceptance/openstack/containerinfra/v1/clustertemplates_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || containerinfra // +build acceptance containerinfra package v1 diff --git a/acceptance/openstack/containerinfra/v1/nodegroups_test.go b/acceptance/openstack/containerinfra/v1/nodegroups_test.go index f5117f1c15..af44bb98eb 100644 --- a/acceptance/openstack/containerinfra/v1/nodegroups_test.go +++ b/acceptance/openstack/containerinfra/v1/nodegroups_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || containerinfra // +build acceptance containerinfra package v1 diff --git a/acceptance/openstack/containerinfra/v1/quotas_test.go b/acceptance/openstack/containerinfra/v1/quotas_test.go index b6e83bcaa1..5783d9195b 100644 --- a/acceptance/openstack/containerinfra/v1/quotas_test.go +++ b/acceptance/openstack/containerinfra/v1/quotas_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || containerinfra // +build acceptance containerinfra package v1 diff --git a/acceptance/openstack/db/v1/configurations_test.go b/acceptance/openstack/db/v1/configurations_test.go index ed5041702c..02472cc8fb 100644 --- a/acceptance/openstack/db/v1/configurations_test.go +++ b/acceptance/openstack/db/v1/configurations_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || db // +build acceptance db package v1 diff --git a/acceptance/openstack/db/v1/databases_test.go b/acceptance/openstack/db/v1/databases_test.go index dcbf72f040..854ca0bc0f 100644 --- a/acceptance/openstack/db/v1/databases_test.go +++ b/acceptance/openstack/db/v1/databases_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || db // +build acceptance db package v1 diff --git a/acceptance/openstack/db/v1/flavors_test.go b/acceptance/openstack/db/v1/flavors_test.go index 73171b9424..0c51565b60 100644 --- a/acceptance/openstack/db/v1/flavors_test.go +++ b/acceptance/openstack/db/v1/flavors_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || db // +build acceptance db package v1 diff --git a/acceptance/openstack/db/v1/instances_test.go b/acceptance/openstack/db/v1/instances_test.go index a98047f3e0..0d9ccdf08f 100644 --- a/acceptance/openstack/db/v1/instances_test.go +++ b/acceptance/openstack/db/v1/instances_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || db // +build acceptance db package v1 diff --git a/acceptance/openstack/db/v1/users_test.go b/acceptance/openstack/db/v1/users_test.go index 4335eabb2f..6bdc00bbad 100644 --- a/acceptance/openstack/db/v1/users_test.go +++ b/acceptance/openstack/db/v1/users_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || db // +build acceptance db package v1 diff --git a/acceptance/openstack/dns/v2/recordsets_test.go b/acceptance/openstack/dns/v2/recordsets_test.go index 885054c0e3..d5df5b9334 100644 --- a/acceptance/openstack/dns/v2/recordsets_test.go +++ b/acceptance/openstack/dns/v2/recordsets_test.go @@ -1,3 +1,4 @@ +//go:build acceptance // +build acceptance package v2 diff --git a/acceptance/openstack/dns/v2/transfers_test.go b/acceptance/openstack/dns/v2/transfers_test.go index ef06284f2e..e1a783a194 100644 --- a/acceptance/openstack/dns/v2/transfers_test.go +++ b/acceptance/openstack/dns/v2/transfers_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || dns || transfers // +build acceptance dns transfers package v2 diff --git a/acceptance/openstack/dns/v2/zones_test.go b/acceptance/openstack/dns/v2/zones_test.go index e07867e9be..ad8206265e 100644 --- a/acceptance/openstack/dns/v2/zones_test.go +++ b/acceptance/openstack/dns/v2/zones_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || dns || zones // +build acceptance dns zones package v2 diff --git a/acceptance/openstack/identity/v2/extension_test.go b/acceptance/openstack/identity/v2/extension_test.go index 593d75ca45..7077c08a84 100644 --- a/acceptance/openstack/identity/v2/extension_test.go +++ b/acceptance/openstack/identity/v2/extension_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || identity // +build acceptance identity package v2 diff --git a/acceptance/openstack/identity/v2/role_test.go b/acceptance/openstack/identity/v2/role_test.go index bc9d26ec34..4c40e70162 100644 --- a/acceptance/openstack/identity/v2/role_test.go +++ b/acceptance/openstack/identity/v2/role_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || identity || roles // +build acceptance identity roles package v2 diff --git a/acceptance/openstack/identity/v2/tenant_test.go b/acceptance/openstack/identity/v2/tenant_test.go index f53270760a..df2dcb7eb0 100644 --- a/acceptance/openstack/identity/v2/tenant_test.go +++ b/acceptance/openstack/identity/v2/tenant_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || identity // +build acceptance identity package v2 diff --git a/acceptance/openstack/identity/v2/token_test.go b/acceptance/openstack/identity/v2/token_test.go index 30ebcc2bf0..cf758c60e2 100644 --- a/acceptance/openstack/identity/v2/token_test.go +++ b/acceptance/openstack/identity/v2/token_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || identity // +build acceptance identity package v2 diff --git a/acceptance/openstack/identity/v2/user_test.go b/acceptance/openstack/identity/v2/user_test.go index caaaaf936a..e700cdd3ec 100644 --- a/acceptance/openstack/identity/v2/user_test.go +++ b/acceptance/openstack/identity/v2/user_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || identity // +build acceptance identity package v2 diff --git a/acceptance/openstack/identity/v3/applicationcredentials_test.go b/acceptance/openstack/identity/v3/applicationcredentials_test.go index d24d0d2356..e9dc615c45 100644 --- a/acceptance/openstack/identity/v3/applicationcredentials_test.go +++ b/acceptance/openstack/identity/v3/applicationcredentials_test.go @@ -1,3 +1,4 @@ +//go:build acceptance // +build acceptance package v3 diff --git a/acceptance/openstack/identity/v3/credentials_test.go b/acceptance/openstack/identity/v3/credentials_test.go index 1dd7e3b5c4..acb5cd94f1 100644 --- a/acceptance/openstack/identity/v3/credentials_test.go +++ b/acceptance/openstack/identity/v3/credentials_test.go @@ -1,3 +1,4 @@ +//go:build acceptance // +build acceptance package v3 diff --git a/acceptance/openstack/identity/v3/domains_test.go b/acceptance/openstack/identity/v3/domains_test.go index 71a335ea88..48ad8247c1 100644 --- a/acceptance/openstack/identity/v3/domains_test.go +++ b/acceptance/openstack/identity/v3/domains_test.go @@ -1,3 +1,4 @@ +//go:build acceptance // +build acceptance package v3 diff --git a/acceptance/openstack/identity/v3/ec2credentials_test.go b/acceptance/openstack/identity/v3/ec2credentials_test.go index 382af904f1..5c19459f9f 100644 --- a/acceptance/openstack/identity/v3/ec2credentials_test.go +++ b/acceptance/openstack/identity/v3/ec2credentials_test.go @@ -1,3 +1,4 @@ +//go:build acceptance // +build acceptance package v3 diff --git a/acceptance/openstack/identity/v3/endpoint_test.go b/acceptance/openstack/identity/v3/endpoint_test.go index 4bc606b34c..eac753d217 100644 --- a/acceptance/openstack/identity/v3/endpoint_test.go +++ b/acceptance/openstack/identity/v3/endpoint_test.go @@ -1,3 +1,4 @@ +//go:build acceptance // +build acceptance package v3 diff --git a/acceptance/openstack/identity/v3/groups_test.go b/acceptance/openstack/identity/v3/groups_test.go index c168817882..b07d7d1b6c 100644 --- a/acceptance/openstack/identity/v3/groups_test.go +++ b/acceptance/openstack/identity/v3/groups_test.go @@ -1,3 +1,4 @@ +//go:build acceptance // +build acceptance package v3 diff --git a/acceptance/openstack/identity/v3/oauth1_test.go b/acceptance/openstack/identity/v3/oauth1_test.go index 02a1d8f10f..848fd64aeb 100644 --- a/acceptance/openstack/identity/v3/oauth1_test.go +++ b/acceptance/openstack/identity/v3/oauth1_test.go @@ -1,3 +1,4 @@ +//go:build acceptance // +build acceptance package v3 diff --git a/acceptance/openstack/identity/v3/policies_test.go b/acceptance/openstack/identity/v3/policies_test.go index 3fb22bb221..01367018b8 100644 --- a/acceptance/openstack/identity/v3/policies_test.go +++ b/acceptance/openstack/identity/v3/policies_test.go @@ -1,3 +1,4 @@ +//go:build acceptance // +build acceptance package v3 diff --git a/acceptance/openstack/identity/v3/projects_test.go b/acceptance/openstack/identity/v3/projects_test.go index c50aaec931..a81cb2d1c1 100644 --- a/acceptance/openstack/identity/v3/projects_test.go +++ b/acceptance/openstack/identity/v3/projects_test.go @@ -1,3 +1,4 @@ +//go:build acceptance // +build acceptance package v3 diff --git a/acceptance/openstack/identity/v3/reauth_test.go b/acceptance/openstack/identity/v3/reauth_test.go index 27363db04e..f34a997adb 100644 --- a/acceptance/openstack/identity/v3/reauth_test.go +++ b/acceptance/openstack/identity/v3/reauth_test.go @@ -1,3 +1,4 @@ +//go:build acceptance // +build acceptance package v3 diff --git a/acceptance/openstack/identity/v3/regions_test.go b/acceptance/openstack/identity/v3/regions_test.go index f4a0b9456b..8bc9d41f02 100644 --- a/acceptance/openstack/identity/v3/regions_test.go +++ b/acceptance/openstack/identity/v3/regions_test.go @@ -1,3 +1,4 @@ +//go:build acceptance // +build acceptance package v3 diff --git a/acceptance/openstack/identity/v3/roles_test.go b/acceptance/openstack/identity/v3/roles_test.go index 799204f6f9..43383e1be1 100644 --- a/acceptance/openstack/identity/v3/roles_test.go +++ b/acceptance/openstack/identity/v3/roles_test.go @@ -1,3 +1,4 @@ +//go:build acceptance // +build acceptance package v3 diff --git a/acceptance/openstack/identity/v3/service_test.go b/acceptance/openstack/identity/v3/service_test.go index 7e072ce3a4..246d9d8ec3 100644 --- a/acceptance/openstack/identity/v3/service_test.go +++ b/acceptance/openstack/identity/v3/service_test.go @@ -1,3 +1,4 @@ +//go:build acceptance // +build acceptance package v3 diff --git a/acceptance/openstack/identity/v3/token_test.go b/acceptance/openstack/identity/v3/token_test.go index ff6e91d49f..85ae0eff67 100644 --- a/acceptance/openstack/identity/v3/token_test.go +++ b/acceptance/openstack/identity/v3/token_test.go @@ -1,3 +1,4 @@ +//go:build acceptance // +build acceptance package v3 diff --git a/acceptance/openstack/identity/v3/trusts_test.go b/acceptance/openstack/identity/v3/trusts_test.go index 6af7cf41dd..abc099e719 100644 --- a/acceptance/openstack/identity/v3/trusts_test.go +++ b/acceptance/openstack/identity/v3/trusts_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || identity || trusts // +build acceptance identity trusts package v3 diff --git a/acceptance/openstack/identity/v3/users_test.go b/acceptance/openstack/identity/v3/users_test.go index 2e283b5ec0..a5edb00854 100644 --- a/acceptance/openstack/identity/v3/users_test.go +++ b/acceptance/openstack/identity/v3/users_test.go @@ -1,3 +1,4 @@ +//go:build acceptance // +build acceptance package v3 diff --git a/acceptance/openstack/imageservice/v2/imageimport_test.go b/acceptance/openstack/imageservice/v2/imageimport_test.go index a5f7f16398..9a9bd4f23e 100644 --- a/acceptance/openstack/imageservice/v2/imageimport_test.go +++ b/acceptance/openstack/imageservice/v2/imageimport_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || imageservice || imageimport // +build acceptance imageservice imageimport package v2 diff --git a/acceptance/openstack/imageservice/v2/images_test.go b/acceptance/openstack/imageservice/v2/images_test.go index ad22bde826..ac6008cef8 100644 --- a/acceptance/openstack/imageservice/v2/images_test.go +++ b/acceptance/openstack/imageservice/v2/images_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || imageservice || images // +build acceptance imageservice images package v2 diff --git a/acceptance/openstack/imageservice/v2/tasks_test.go b/acceptance/openstack/imageservice/v2/tasks_test.go index 1e0fbf3f2d..b06980c761 100644 --- a/acceptance/openstack/imageservice/v2/tasks_test.go +++ b/acceptance/openstack/imageservice/v2/tasks_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || imageservice || tasks // +build acceptance imageservice tasks package v2 diff --git a/acceptance/openstack/keymanager/v1/acls_test.go b/acceptance/openstack/keymanager/v1/acls_test.go index b3de4f54ab..1ea443bb3c 100644 --- a/acceptance/openstack/keymanager/v1/acls_test.go +++ b/acceptance/openstack/keymanager/v1/acls_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || keymanager || acls // +build acceptance keymanager acls package v1 diff --git a/acceptance/openstack/keymanager/v1/containers_test.go b/acceptance/openstack/keymanager/v1/containers_test.go index d8bb78970d..f369eef44e 100644 --- a/acceptance/openstack/keymanager/v1/containers_test.go +++ b/acceptance/openstack/keymanager/v1/containers_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || keymanager || containers // +build acceptance keymanager containers package v1 diff --git a/acceptance/openstack/keymanager/v1/orders_test.go b/acceptance/openstack/keymanager/v1/orders_test.go index 5105bdbc55..e74ad4d720 100644 --- a/acceptance/openstack/keymanager/v1/orders_test.go +++ b/acceptance/openstack/keymanager/v1/orders_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || keymanager || orders // +build acceptance keymanager orders package v1 diff --git a/acceptance/openstack/keymanager/v1/secrets_test.go b/acceptance/openstack/keymanager/v1/secrets_test.go index fffed0fe7a..d46f8e62d5 100644 --- a/acceptance/openstack/keymanager/v1/secrets_test.go +++ b/acceptance/openstack/keymanager/v1/secrets_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || keymanager || secrets // +build acceptance keymanager secrets package v1 diff --git a/acceptance/openstack/loadbalancer/v2/amphorae_test.go b/acceptance/openstack/loadbalancer/v2/amphorae_test.go index 97151ea7a0..b476ea5097 100644 --- a/acceptance/openstack/loadbalancer/v2/amphorae_test.go +++ b/acceptance/openstack/loadbalancer/v2/amphorae_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || containers || capsules // +build acceptance containers capsules package v2 diff --git a/acceptance/openstack/loadbalancer/v2/l7policies_test.go b/acceptance/openstack/loadbalancer/v2/l7policies_test.go index 9e2e899e34..41de6e9e65 100644 --- a/acceptance/openstack/loadbalancer/v2/l7policies_test.go +++ b/acceptance/openstack/loadbalancer/v2/l7policies_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || networking || loadbalancer || l7policies // +build acceptance networking loadbalancer l7policies package v2 diff --git a/acceptance/openstack/loadbalancer/v2/listeners_test.go b/acceptance/openstack/loadbalancer/v2/listeners_test.go index df470f922f..60828cae39 100644 --- a/acceptance/openstack/loadbalancer/v2/listeners_test.go +++ b/acceptance/openstack/loadbalancer/v2/listeners_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || networking || loadbalancer || listeners // +build acceptance networking loadbalancer listeners package v2 diff --git a/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go b/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go index d1808f6539..6d4ba3447e 100644 --- a/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go +++ b/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || networking || loadbalancer || loadbalancers // +build acceptance networking loadbalancer loadbalancers package v2 diff --git a/acceptance/openstack/loadbalancer/v2/monitors_test.go b/acceptance/openstack/loadbalancer/v2/monitors_test.go index e13c453eec..c55d937983 100644 --- a/acceptance/openstack/loadbalancer/v2/monitors_test.go +++ b/acceptance/openstack/loadbalancer/v2/monitors_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || networking || loadbalancer || monitors // +build acceptance networking loadbalancer monitors package v2 diff --git a/acceptance/openstack/loadbalancer/v2/pools_test.go b/acceptance/openstack/loadbalancer/v2/pools_test.go index c2174c3c2e..2f8b39b036 100644 --- a/acceptance/openstack/loadbalancer/v2/pools_test.go +++ b/acceptance/openstack/loadbalancer/v2/pools_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || networking || loadbalancer || pools // +build acceptance networking loadbalancer pools package v2 diff --git a/acceptance/openstack/loadbalancer/v2/providers_test.go b/acceptance/openstack/loadbalancer/v2/providers_test.go index 4426150f60..7c1e42217d 100644 --- a/acceptance/openstack/loadbalancer/v2/providers_test.go +++ b/acceptance/openstack/loadbalancer/v2/providers_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || networking || loadbalancer || providers // +build acceptance networking loadbalancer providers package v2 diff --git a/acceptance/openstack/loadbalancer/v2/quotas_test.go b/acceptance/openstack/loadbalancer/v2/quotas_test.go index fa359dd7b7..a309dbb62b 100644 --- a/acceptance/openstack/loadbalancer/v2/quotas_test.go +++ b/acceptance/openstack/loadbalancer/v2/quotas_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || networking || loadbalancer || quotas // +build acceptance networking loadbalancer quotas package v2 diff --git a/acceptance/openstack/messaging/v2/claims_test.go b/acceptance/openstack/messaging/v2/claims_test.go index 4ffb9229e9..32fa87862f 100644 --- a/acceptance/openstack/messaging/v2/claims_test.go +++ b/acceptance/openstack/messaging/v2/claims_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || messaging || claims // +build acceptance messaging claims package v2 diff --git a/acceptance/openstack/messaging/v2/message_test.go b/acceptance/openstack/messaging/v2/message_test.go index f3c558116e..7f6f09b9bd 100644 --- a/acceptance/openstack/messaging/v2/message_test.go +++ b/acceptance/openstack/messaging/v2/message_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || messaging || messages // +build acceptance messaging messages package v2 diff --git a/acceptance/openstack/messaging/v2/queue_test.go b/acceptance/openstack/messaging/v2/queue_test.go index 166f46e83d..c34d104ef5 100644 --- a/acceptance/openstack/messaging/v2/queue_test.go +++ b/acceptance/openstack/messaging/v2/queue_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || messaging || queues // +build acceptance messaging queues package v2 diff --git a/acceptance/openstack/networking/v2/apiversion_test.go b/acceptance/openstack/networking/v2/apiversion_test.go index 2fb4a23210..9ad3e9212b 100644 --- a/acceptance/openstack/networking/v2/apiversion_test.go +++ b/acceptance/openstack/networking/v2/apiversion_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || networking // +build acceptance networking package v2 diff --git a/acceptance/openstack/networking/v2/extension_test.go b/acceptance/openstack/networking/v2/extension_test.go index 5609e85261..4fed3e1fd9 100644 --- a/acceptance/openstack/networking/v2/extension_test.go +++ b/acceptance/openstack/networking/v2/extension_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || networking || extensions // +build acceptance networking extensions package v2 diff --git a/acceptance/openstack/networking/v2/extensions/agents/agents_test.go b/acceptance/openstack/networking/v2/extensions/agents/agents_test.go index 6436b27b4e..c97a50e69d 100644 --- a/acceptance/openstack/networking/v2/extensions/agents/agents_test.go +++ b/acceptance/openstack/networking/v2/extensions/agents/agents_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || networking || agents // +build acceptance networking agents package agents diff --git a/acceptance/openstack/networking/v2/extensions/attributestags_test.go b/acceptance/openstack/networking/v2/extensions/attributestags_test.go index 3484e6a912..573b181853 100644 --- a/acceptance/openstack/networking/v2/extensions/attributestags_test.go +++ b/acceptance/openstack/networking/v2/extensions/attributestags_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || networking || tags // +build acceptance networking tags package extensions diff --git a/acceptance/openstack/networking/v2/extensions/dns/dns_test.go b/acceptance/openstack/networking/v2/extensions/dns/dns_test.go index 0cb62551c5..0a620bb579 100644 --- a/acceptance/openstack/networking/v2/extensions/dns/dns_test.go +++ b/acceptance/openstack/networking/v2/extensions/dns/dns_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || networking // +build acceptance networking package dns diff --git a/acceptance/openstack/networking/v2/extensions/fwaas/firewall_test.go b/acceptance/openstack/networking/v2/extensions/fwaas/firewall_test.go index 4779491fda..1af25efa43 100644 --- a/acceptance/openstack/networking/v2/extensions/fwaas/firewall_test.go +++ b/acceptance/openstack/networking/v2/extensions/fwaas/firewall_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || networking || fwaas // +build acceptance networking fwaas package fwaas diff --git a/acceptance/openstack/networking/v2/extensions/fwaas/policy_test.go b/acceptance/openstack/networking/v2/extensions/fwaas/policy_test.go index ab0d7c9008..b075e794be 100644 --- a/acceptance/openstack/networking/v2/extensions/fwaas/policy_test.go +++ b/acceptance/openstack/networking/v2/extensions/fwaas/policy_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || networking || fwaas // +build acceptance networking fwaas package fwaas diff --git a/acceptance/openstack/networking/v2/extensions/fwaas/rule_test.go b/acceptance/openstack/networking/v2/extensions/fwaas/rule_test.go index 6f5968b30c..be32806c96 100644 --- a/acceptance/openstack/networking/v2/extensions/fwaas/rule_test.go +++ b/acceptance/openstack/networking/v2/extensions/fwaas/rule_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || networking || fwaas // +build acceptance networking fwaas package fwaas diff --git a/acceptance/openstack/networking/v2/extensions/fwaas_v2/groups_test.go b/acceptance/openstack/networking/v2/extensions/fwaas_v2/groups_test.go index d58562580f..a6a0bcc36e 100644 --- a/acceptance/openstack/networking/v2/extensions/fwaas_v2/groups_test.go +++ b/acceptance/openstack/networking/v2/extensions/fwaas_v2/groups_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || networking || fwaas_v2 // +build acceptance networking fwaas_v2 package fwaas_v2 diff --git a/acceptance/openstack/networking/v2/extensions/fwaas_v2/policy_test.go b/acceptance/openstack/networking/v2/extensions/fwaas_v2/policy_test.go index fc3ec1a1da..2a86e78b6e 100644 --- a/acceptance/openstack/networking/v2/extensions/fwaas_v2/policy_test.go +++ b/acceptance/openstack/networking/v2/extensions/fwaas_v2/policy_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || networking || fwaas_v2 // +build acceptance networking fwaas_v2 package fwaas_v2 diff --git a/acceptance/openstack/networking/v2/extensions/fwaas_v2/rule_test.go b/acceptance/openstack/networking/v2/extensions/fwaas_v2/rule_test.go index 06689f196d..f9db68d5ae 100644 --- a/acceptance/openstack/networking/v2/extensions/fwaas_v2/rule_test.go +++ b/acceptance/openstack/networking/v2/extensions/fwaas_v2/rule_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || networking || fwaas_v2 // +build acceptance networking fwaas_v2 package fwaas_v2 diff --git a/acceptance/openstack/networking/v2/extensions/layer3/addressscopes_test.go b/acceptance/openstack/networking/v2/extensions/layer3/addressscopes_test.go index 4d7cff3538..1687b148b3 100644 --- a/acceptance/openstack/networking/v2/extensions/layer3/addressscopes_test.go +++ b/acceptance/openstack/networking/v2/extensions/layer3/addressscopes_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || networking || layer3 || addressscopes // +build acceptance networking layer3 addressscopes package layer3 diff --git a/acceptance/openstack/networking/v2/extensions/layer3/floatingips_test.go b/acceptance/openstack/networking/v2/extensions/layer3/floatingips_test.go index 5d87120bbc..81d1d18f05 100644 --- a/acceptance/openstack/networking/v2/extensions/layer3/floatingips_test.go +++ b/acceptance/openstack/networking/v2/extensions/layer3/floatingips_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || networking || layer3 || floatingips // +build acceptance networking layer3 floatingips package layer3 diff --git a/acceptance/openstack/networking/v2/extensions/layer3/routers_test.go b/acceptance/openstack/networking/v2/extensions/layer3/routers_test.go index 0bbeb9fa11..bf26bb44e4 100644 --- a/acceptance/openstack/networking/v2/extensions/layer3/routers_test.go +++ b/acceptance/openstack/networking/v2/extensions/layer3/routers_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || networking || layer3 || router // +build acceptance networking layer3 router package layer3 diff --git a/acceptance/openstack/networking/v2/extensions/lbaas/members_test.go b/acceptance/openstack/networking/v2/extensions/lbaas/members_test.go index c57bc7ecc6..7770dbf422 100644 --- a/acceptance/openstack/networking/v2/extensions/lbaas/members_test.go +++ b/acceptance/openstack/networking/v2/extensions/lbaas/members_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || networking || lbaas || member // +build acceptance networking lbaas member package lbaas diff --git a/acceptance/openstack/networking/v2/extensions/lbaas/monitors_test.go b/acceptance/openstack/networking/v2/extensions/lbaas/monitors_test.go index 31ce3fad98..e2027414a6 100644 --- a/acceptance/openstack/networking/v2/extensions/lbaas/monitors_test.go +++ b/acceptance/openstack/networking/v2/extensions/lbaas/monitors_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || networking || lbaas || monitors // +build acceptance networking lbaas monitors package lbaas diff --git a/acceptance/openstack/networking/v2/extensions/lbaas/pools_test.go b/acceptance/openstack/networking/v2/extensions/lbaas/pools_test.go index e1eb940678..82a49056a7 100644 --- a/acceptance/openstack/networking/v2/extensions/lbaas/pools_test.go +++ b/acceptance/openstack/networking/v2/extensions/lbaas/pools_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || networking || lbaas || pool // +build acceptance networking lbaas pool package lbaas diff --git a/acceptance/openstack/networking/v2/extensions/lbaas/vips_test.go b/acceptance/openstack/networking/v2/extensions/lbaas/vips_test.go index 4e54170e70..b691191943 100644 --- a/acceptance/openstack/networking/v2/extensions/lbaas/vips_test.go +++ b/acceptance/openstack/networking/v2/extensions/lbaas/vips_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || networking || lbaas || vip // +build acceptance networking lbaas vip package lbaas diff --git a/acceptance/openstack/networking/v2/extensions/lbaas_v2/l7policies_test.go b/acceptance/openstack/networking/v2/extensions/lbaas_v2/l7policies_test.go index fc7c3d9e36..85a232b7cf 100644 --- a/acceptance/openstack/networking/v2/extensions/lbaas_v2/l7policies_test.go +++ b/acceptance/openstack/networking/v2/extensions/lbaas_v2/l7policies_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || networking || loadbalancer || l7policies // +build acceptance networking loadbalancer l7policies package lbaas_v2 diff --git a/acceptance/openstack/networking/v2/extensions/lbaas_v2/listeners_test.go b/acceptance/openstack/networking/v2/extensions/lbaas_v2/listeners_test.go index 30136b0494..cc530d0dde 100644 --- a/acceptance/openstack/networking/v2/extensions/lbaas_v2/listeners_test.go +++ b/acceptance/openstack/networking/v2/extensions/lbaas_v2/listeners_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || networking || loadbalancer || listeners // +build acceptance networking loadbalancer listeners package lbaas_v2 diff --git a/acceptance/openstack/networking/v2/extensions/lbaas_v2/loadbalancers_test.go b/acceptance/openstack/networking/v2/extensions/lbaas_v2/loadbalancers_test.go index c17058d597..061a30ef59 100644 --- a/acceptance/openstack/networking/v2/extensions/lbaas_v2/loadbalancers_test.go +++ b/acceptance/openstack/networking/v2/extensions/lbaas_v2/loadbalancers_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || networking || lbaas_v2 || loadbalancers // +build acceptance networking lbaas_v2 loadbalancers package lbaas_v2 diff --git a/acceptance/openstack/networking/v2/extensions/lbaas_v2/monitors_test.go b/acceptance/openstack/networking/v2/extensions/lbaas_v2/monitors_test.go index 84b0c867d7..37125ee088 100644 --- a/acceptance/openstack/networking/v2/extensions/lbaas_v2/monitors_test.go +++ b/acceptance/openstack/networking/v2/extensions/lbaas_v2/monitors_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || networking || loadbalancer || monitors // +build acceptance networking loadbalancer monitors package lbaas_v2 diff --git a/acceptance/openstack/networking/v2/extensions/lbaas_v2/pools_test.go b/acceptance/openstack/networking/v2/extensions/lbaas_v2/pools_test.go index bcab7fd55c..43ba94b67f 100644 --- a/acceptance/openstack/networking/v2/extensions/lbaas_v2/pools_test.go +++ b/acceptance/openstack/networking/v2/extensions/lbaas_v2/pools_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || networking || loadbalancer || pools // +build acceptance networking loadbalancer pools package lbaas_v2 diff --git a/acceptance/openstack/networking/v2/extensions/mtu/mtu_test.go b/acceptance/openstack/networking/v2/extensions/mtu/mtu_test.go index 47dd195153..21dfc56feb 100644 --- a/acceptance/openstack/networking/v2/extensions/mtu/mtu_test.go +++ b/acceptance/openstack/networking/v2/extensions/mtu/mtu_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || networking // +build acceptance networking package mtu diff --git a/acceptance/openstack/networking/v2/extensions/networkipavailabilities/networkipavailabilities_test.go b/acceptance/openstack/networking/v2/extensions/networkipavailabilities/networkipavailabilities_test.go index cfebed0a30..14b7106e29 100644 --- a/acceptance/openstack/networking/v2/extensions/networkipavailabilities/networkipavailabilities_test.go +++ b/acceptance/openstack/networking/v2/extensions/networkipavailabilities/networkipavailabilities_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || networking || networkipavailabilities // +build acceptance networking networkipavailabilities package networkipavailabilities diff --git a/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding_test.go b/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding_test.go index d699c786a6..8ab86b9ca9 100644 --- a/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding_test.go +++ b/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || networking // +build acceptance networking package portsbinding diff --git a/acceptance/openstack/networking/v2/extensions/provider_test.go b/acceptance/openstack/networking/v2/extensions/provider_test.go index 45893fbd33..9e8f622d47 100644 --- a/acceptance/openstack/networking/v2/extensions/provider_test.go +++ b/acceptance/openstack/networking/v2/extensions/provider_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || networking || provider // +build acceptance networking provider package extensions diff --git a/acceptance/openstack/networking/v2/extensions/qos/policies/policies_test.go b/acceptance/openstack/networking/v2/extensions/qos/policies/policies_test.go index 85ad63adf1..ae1fd190e2 100644 --- a/acceptance/openstack/networking/v2/extensions/qos/policies/policies_test.go +++ b/acceptance/openstack/networking/v2/extensions/qos/policies/policies_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || networking || qos || policies // +build acceptance networking qos policies package policies diff --git a/acceptance/openstack/networking/v2/extensions/quotas/quotas_test.go b/acceptance/openstack/networking/v2/extensions/quotas/quotas_test.go index 048e93e5f5..c462cd3338 100644 --- a/acceptance/openstack/networking/v2/extensions/quotas/quotas_test.go +++ b/acceptance/openstack/networking/v2/extensions/quotas/quotas_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || networking || quotas // +build acceptance networking quotas package quotas diff --git a/acceptance/openstack/networking/v2/extensions/rbacpolicies/rbacpolicies_test.go b/acceptance/openstack/networking/v2/extensions/rbacpolicies/rbacpolicies_test.go index b07bab1718..7b071cb304 100644 --- a/acceptance/openstack/networking/v2/extensions/rbacpolicies/rbacpolicies_test.go +++ b/acceptance/openstack/networking/v2/extensions/rbacpolicies/rbacpolicies_test.go @@ -1,3 +1,4 @@ +//go:build acceptance // +build acceptance package rbacpolicies diff --git a/acceptance/openstack/networking/v2/extensions/security_test.go b/acceptance/openstack/networking/v2/extensions/security_test.go index 3bf44a8071..967177b8cb 100644 --- a/acceptance/openstack/networking/v2/extensions/security_test.go +++ b/acceptance/openstack/networking/v2/extensions/security_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || networking || security // +build acceptance networking security package extensions diff --git a/acceptance/openstack/networking/v2/extensions/subnetpools/subnetpools_test.go b/acceptance/openstack/networking/v2/extensions/subnetpools/subnetpools_test.go index e5c9c320ed..0989300019 100644 --- a/acceptance/openstack/networking/v2/extensions/subnetpools/subnetpools_test.go +++ b/acceptance/openstack/networking/v2/extensions/subnetpools/subnetpools_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || networking || subnetpools // +build acceptance networking subnetpools package v2 diff --git a/acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go b/acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go index 672508cc50..50b02d7b30 100644 --- a/acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go +++ b/acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || trunks // +build acceptance trunks package trunks diff --git a/acceptance/openstack/networking/v2/extensions/vlantransparent/vlantransparent_test.go b/acceptance/openstack/networking/v2/extensions/vlantransparent/vlantransparent_test.go index 248ab04ffa..fc6df0625c 100644 --- a/acceptance/openstack/networking/v2/extensions/vlantransparent/vlantransparent_test.go +++ b/acceptance/openstack/networking/v2/extensions/vlantransparent/vlantransparent_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || networking || vlantransparent // +build acceptance networking vlantransparent package v2 diff --git a/acceptance/openstack/networking/v2/extensions/vpnaas/group_test.go b/acceptance/openstack/networking/v2/extensions/vpnaas/group_test.go index c45daf0e43..fafd6df496 100644 --- a/acceptance/openstack/networking/v2/extensions/vpnaas/group_test.go +++ b/acceptance/openstack/networking/v2/extensions/vpnaas/group_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || networking || vpnaas // +build acceptance networking vpnaas package vpnaas diff --git a/acceptance/openstack/networking/v2/extensions/vpnaas/ikepolicy_test.go b/acceptance/openstack/networking/v2/extensions/vpnaas/ikepolicy_test.go index c14c9fb5e3..0c2715d9ab 100644 --- a/acceptance/openstack/networking/v2/extensions/vpnaas/ikepolicy_test.go +++ b/acceptance/openstack/networking/v2/extensions/vpnaas/ikepolicy_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || networking || vpnaas // +build acceptance networking vpnaas package vpnaas diff --git a/acceptance/openstack/networking/v2/extensions/vpnaas/ipsecpolicy_test.go b/acceptance/openstack/networking/v2/extensions/vpnaas/ipsecpolicy_test.go index 7589590ee4..d0d5729693 100644 --- a/acceptance/openstack/networking/v2/extensions/vpnaas/ipsecpolicy_test.go +++ b/acceptance/openstack/networking/v2/extensions/vpnaas/ipsecpolicy_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || networking || vpnaas // +build acceptance networking vpnaas package vpnaas diff --git a/acceptance/openstack/networking/v2/extensions/vpnaas/service_test.go b/acceptance/openstack/networking/v2/extensions/vpnaas/service_test.go index d7bc05ce5e..5c62403e23 100644 --- a/acceptance/openstack/networking/v2/extensions/vpnaas/service_test.go +++ b/acceptance/openstack/networking/v2/extensions/vpnaas/service_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || networking || fwaas // +build acceptance networking fwaas package vpnaas diff --git a/acceptance/openstack/networking/v2/extensions/vpnaas/siteconnection_test.go b/acceptance/openstack/networking/v2/extensions/vpnaas/siteconnection_test.go index 72d025ea7a..b9ce51b099 100644 --- a/acceptance/openstack/networking/v2/extensions/vpnaas/siteconnection_test.go +++ b/acceptance/openstack/networking/v2/extensions/vpnaas/siteconnection_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || networking || vpnaas // +build acceptance networking vpnaas package vpnaas diff --git a/acceptance/openstack/networking/v2/networks_test.go b/acceptance/openstack/networking/v2/networks_test.go index e00ab0a32e..26ebee0855 100644 --- a/acceptance/openstack/networking/v2/networks_test.go +++ b/acceptance/openstack/networking/v2/networks_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || networking // +build acceptance networking package v2 diff --git a/acceptance/openstack/networking/v2/ports_test.go b/acceptance/openstack/networking/v2/ports_test.go index 6355e99491..966b42db7c 100644 --- a/acceptance/openstack/networking/v2/ports_test.go +++ b/acceptance/openstack/networking/v2/ports_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || networking // +build acceptance networking package v2 diff --git a/acceptance/openstack/networking/v2/subnets_test.go b/acceptance/openstack/networking/v2/subnets_test.go index 8b96293451..104047cad5 100644 --- a/acceptance/openstack/networking/v2/subnets_test.go +++ b/acceptance/openstack/networking/v2/subnets_test.go @@ -1,3 +1,4 @@ +//go:build acceptance || networking // +build acceptance networking package v2 diff --git a/acceptance/openstack/objectstorage/v1/accounts_test.go b/acceptance/openstack/objectstorage/v1/accounts_test.go index bb9745f835..a484dc1601 100644 --- a/acceptance/openstack/objectstorage/v1/accounts_test.go +++ b/acceptance/openstack/objectstorage/v1/accounts_test.go @@ -1,3 +1,4 @@ +//go:build acceptance // +build acceptance package v1 diff --git a/acceptance/openstack/objectstorage/v1/containers_test.go b/acceptance/openstack/objectstorage/v1/containers_test.go index ea12218b27..d754fe3a59 100644 --- a/acceptance/openstack/objectstorage/v1/containers_test.go +++ b/acceptance/openstack/objectstorage/v1/containers_test.go @@ -1,3 +1,4 @@ +//go:build acceptance // +build acceptance package v1 diff --git a/acceptance/openstack/objectstorage/v1/objects_test.go b/acceptance/openstack/objectstorage/v1/objects_test.go index 8683b442bd..384a2d3b37 100644 --- a/acceptance/openstack/objectstorage/v1/objects_test.go +++ b/acceptance/openstack/objectstorage/v1/objects_test.go @@ -1,3 +1,4 @@ +//go:build acceptance // +build acceptance package v1 diff --git a/acceptance/openstack/orchestration/v1/buildinfo_test.go b/acceptance/openstack/orchestration/v1/buildinfo_test.go index c9564f22b9..afbe2172ec 100644 --- a/acceptance/openstack/orchestration/v1/buildinfo_test.go +++ b/acceptance/openstack/orchestration/v1/buildinfo_test.go @@ -1,3 +1,4 @@ +//go:build acceptance // +build acceptance package v1 diff --git a/acceptance/openstack/orchestration/v1/stackevents_test.go b/acceptance/openstack/orchestration/v1/stackevents_test.go index 577160e03e..65eb4cbf5e 100644 --- a/acceptance/openstack/orchestration/v1/stackevents_test.go +++ b/acceptance/openstack/orchestration/v1/stackevents_test.go @@ -1,3 +1,4 @@ +//go:build acceptance // +build acceptance package v1 diff --git a/acceptance/openstack/orchestration/v1/stackresources_test.go b/acceptance/openstack/orchestration/v1/stackresources_test.go index 91ff630ec9..cbe5a44c8e 100644 --- a/acceptance/openstack/orchestration/v1/stackresources_test.go +++ b/acceptance/openstack/orchestration/v1/stackresources_test.go @@ -1,3 +1,4 @@ +//go:build acceptance // +build acceptance package v1 diff --git a/acceptance/openstack/orchestration/v1/stacks_test.go b/acceptance/openstack/orchestration/v1/stacks_test.go index 559b365d4c..e9b0ee4321 100644 --- a/acceptance/openstack/orchestration/v1/stacks_test.go +++ b/acceptance/openstack/orchestration/v1/stacks_test.go @@ -1,3 +1,4 @@ +//go:build acceptance // +build acceptance package v1 diff --git a/acceptance/openstack/orchestration/v1/stacktemplates_test.go b/acceptance/openstack/orchestration/v1/stacktemplates_test.go index 98314825a2..d1eed484f5 100644 --- a/acceptance/openstack/orchestration/v1/stacktemplates_test.go +++ b/acceptance/openstack/orchestration/v1/stacktemplates_test.go @@ -1,3 +1,4 @@ +//go:build acceptance // +build acceptance package v1 diff --git a/acceptance/openstack/pkg.go b/acceptance/openstack/pkg.go index ef11064a4e..caec0ab6ef 100644 --- a/acceptance/openstack/pkg.go +++ b/acceptance/openstack/pkg.go @@ -1,3 +1,4 @@ +//go:build acceptance // +build acceptance package openstack diff --git a/acceptance/openstack/sharedfilesystems/v2/availabilityzones_test.go b/acceptance/openstack/sharedfilesystems/v2/availabilityzones_test.go index 2f2b80886c..ed42cdfef9 100644 --- a/acceptance/openstack/sharedfilesystems/v2/availabilityzones_test.go +++ b/acceptance/openstack/sharedfilesystems/v2/availabilityzones_test.go @@ -1,3 +1,4 @@ +//go:build acceptance // +build acceptance package v2 diff --git a/acceptance/openstack/sharedfilesystems/v2/securityservices_test.go b/acceptance/openstack/sharedfilesystems/v2/securityservices_test.go index af3e537530..e0e1b16f68 100644 --- a/acceptance/openstack/sharedfilesystems/v2/securityservices_test.go +++ b/acceptance/openstack/sharedfilesystems/v2/securityservices_test.go @@ -1,3 +1,4 @@ +//go:build acceptance // +build acceptance package v2 diff --git a/acceptance/openstack/sharedfilesystems/v2/sharenetworks_test.go b/acceptance/openstack/sharedfilesystems/v2/sharenetworks_test.go index 02d2940c55..bcd3f78a89 100644 --- a/acceptance/openstack/sharedfilesystems/v2/sharenetworks_test.go +++ b/acceptance/openstack/sharedfilesystems/v2/sharenetworks_test.go @@ -1,3 +1,4 @@ +//go:build acceptance // +build acceptance package v2 diff --git a/acceptance/openstack/sharedfilesystems/v2/shares_test.go b/acceptance/openstack/sharedfilesystems/v2/shares_test.go index 972c5fbd12..87fd8e3c2b 100644 --- a/acceptance/openstack/sharedfilesystems/v2/shares_test.go +++ b/acceptance/openstack/sharedfilesystems/v2/shares_test.go @@ -1,3 +1,4 @@ +//go:build acceptance // +build acceptance package v2 diff --git a/acceptance/openstack/sharedfilesystems/v2/sharetypes_test.go b/acceptance/openstack/sharedfilesystems/v2/sharetypes_test.go index 470d15bbcf..0fa951d701 100644 --- a/acceptance/openstack/sharedfilesystems/v2/sharetypes_test.go +++ b/acceptance/openstack/sharedfilesystems/v2/sharetypes_test.go @@ -1,3 +1,4 @@ +//go:build acceptance // +build acceptance package v2 diff --git a/acceptance/openstack/sharedfilesystems/v2/snapshots_test.go b/acceptance/openstack/sharedfilesystems/v2/snapshots_test.go index 69d44809f0..190534dc3d 100644 --- a/acceptance/openstack/sharedfilesystems/v2/snapshots_test.go +++ b/acceptance/openstack/sharedfilesystems/v2/snapshots_test.go @@ -1,3 +1,4 @@ +//go:build acceptance // +build acceptance package v2 From 1200e7d9078c475e538efd90065c3468690fb121 Mon Sep 17 00:00:00 2001 From: Emilien Macchi <emilien@redhat.com> Date: Fri, 10 Dec 2021 15:16:06 -0500 Subject: [PATCH 018/360] Update changelog to prepare the next release (0.24) --- CHANGELOG.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a798c68d8..25b52a4fb4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,19 @@ +## 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 From c3ecbfec83d40e0909f649039228d3199e3956a8 Mon Sep 17 00:00:00 2001 From: Ludovic Lamarche <ludovic.lamarche@ovhcloud.com> Date: Thu, 30 Dec 2021 15:37:18 +0000 Subject: [PATCH 019/360] implement OS-EP-FILTER project endpoints api --- .../v3/extensions/projectendpoints/doc.go | 27 ++++ .../extensions/projectendpoints/requests.go | 33 +++++ .../v3/extensions/projectendpoints/results.go | 57 +++++++++ .../projectendpoints/testing/doc.go | 2 + .../projectendpoints/testing/requests_test.go | 116 ++++++++++++++++++ .../v3/extensions/projectendpoints/urls.go | 15 +++ 6 files changed, 250 insertions(+) create mode 100644 openstack/identity/v3/extensions/projectendpoints/doc.go create mode 100644 openstack/identity/v3/extensions/projectendpoints/requests.go create mode 100644 openstack/identity/v3/extensions/projectendpoints/results.go create mode 100644 openstack/identity/v3/extensions/projectendpoints/testing/doc.go create mode 100644 openstack/identity/v3/extensions/projectendpoints/testing/requests_test.go create mode 100644 openstack/identity/v3/extensions/projectendpoints/urls.go diff --git a/openstack/identity/v3/extensions/projectendpoints/doc.go b/openstack/identity/v3/extensions/projectendpoints/doc.go new file mode 100644 index 0000000000..10cdd2c136 --- /dev/null +++ b/openstack/identity/v3/extensions/projectendpoints/doc.go @@ -0,0 +1,27 @@ +/* +Package endpoints provides information and interaction with the service +OS-EP-FILTER/endpoints API resource in the OpenStack Identity service. + +For more information, see: +https://docs.openstack.org/api-ref/identity/v3-ext/#list-associations-by-project + +Example to List Project Endpoints + + projectD := "e629d6e599d9489fb3ae5d9cc12eaea3" + + allPages, err := projectendpoints.List(identityClient, projectID).AllPages() + if err != nil { + panic(err) + } + + allEndpoints, err := projectendpoints.ExtractEndpoints(allPages) + if err != nil { + panic(err) + } + + for _, endpoint := range allEndpoints { + fmt.Printf("%+v\n", endpoint) + } + +*/ +package projectendpoints diff --git a/openstack/identity/v3/extensions/projectendpoints/requests.go b/openstack/identity/v3/extensions/projectendpoints/requests.go new file mode 100644 index 0000000000..771dd8f522 --- /dev/null +++ b/openstack/identity/v3/extensions/projectendpoints/requests.go @@ -0,0 +1,33 @@ +package projectendpoints + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +type CreateOptsBuilder interface { + ToEndpointCreateMap() (map[string]interface{}, error) +} + +// Create inserts a new Endpoint association to a project. +func Create(client *gophercloud.ServiceClient, projectID, endpointID string) (r CreateResult) { + resp, err := client.Put(createURL(client, projectID, endpointID), nil, nil, &gophercloud.RequestOpts{OkCodes: []int{204}}) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// List enumerates endpoints in a paginated collection, optionally filtered +// by ListOpts criteria. +func List(client *gophercloud.ServiceClient, projectID string) pagination.Pager { + u := listURL(client, projectID) + return pagination.NewPager(client, u, func(r pagination.PageResult) pagination.Page { + return EndpointPage{pagination.LinkedPageBase{PageResult: r}} + }) +} + +// Delete removes an endpoint from the service catalog. +func Delete(client *gophercloud.ServiceClient, projectID string, endpointID string) (r DeleteResult) { + resp, err := client.Delete(deleteURL(client, projectID, endpointID), &gophercloud.RequestOpts{OkCodes: []int{204}}) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} diff --git a/openstack/identity/v3/extensions/projectendpoints/results.go b/openstack/identity/v3/extensions/projectendpoints/results.go new file mode 100644 index 0000000000..029e58f184 --- /dev/null +++ b/openstack/identity/v3/extensions/projectendpoints/results.go @@ -0,0 +1,57 @@ +package projectendpoints + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// CreateResult is the response from a Create operation. Call its Extract +// method to interpret it as an Endpoint. +type CreateResult struct { + gophercloud.ErrResult +} + +// DeleteResult is the response from a Delete operation. Call its ExtractErr +// method to determine if the call succeeded or failed. +type DeleteResult struct { + gophercloud.ErrResult +} + +// Endpoint describes the entry point for another service's API. +type Endpoint struct { + // ID is the unique ID of the endpoint. + ID string `json:"id"` + + // Availability is the interface type of the Endpoint (admin, internal, + // or public), referenced by the gophercloud.Availability type. + Availability gophercloud.Availability `json:"interface"` + + // Region is the region the Endpoint is located in. + Region string `json:"region"` + + // ServiceID is the ID of the service the Endpoint refers to. + ServiceID string `json:"service_id"` + + // URL is the url of the Endpoint. + URL string `json:"url"` +} + +// EndpointPage is a single page of Endpoint results. +type EndpointPage struct { + pagination.LinkedPageBase +} + +// IsEmpty returns true if no Endpoints were returned. +func (r EndpointPage) IsEmpty() (bool, error) { + es, err := ExtractEndpoints(r) + return len(es) == 0, err +} + +// ExtractEndpoints extracts an Endpoint slice from a Page. +func ExtractEndpoints(r pagination.Page) ([]Endpoint, error) { + var s struct { + Endpoints []Endpoint `json:"endpoints"` + } + err := (r.(EndpointPage)).ExtractInto(&s) + return s.Endpoints, err +} diff --git a/openstack/identity/v3/extensions/projectendpoints/testing/doc.go b/openstack/identity/v3/extensions/projectendpoints/testing/doc.go new file mode 100644 index 0000000000..1c748e2b24 --- /dev/null +++ b/openstack/identity/v3/extensions/projectendpoints/testing/doc.go @@ -0,0 +1,2 @@ +// projectendpoints unit tests +package testing diff --git a/openstack/identity/v3/extensions/projectendpoints/testing/requests_test.go b/openstack/identity/v3/extensions/projectendpoints/testing/requests_test.go new file mode 100644 index 0000000000..1b88765eff --- /dev/null +++ b/openstack/identity/v3/extensions/projectendpoints/testing/requests_test.go @@ -0,0 +1,116 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/projectendpoints" + "github.com/gophercloud/gophercloud/pagination" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestCreateSuccessful(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/OS-EP-FILTER/projects/project-id/endpoints/endpoint-id", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PUT") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + w.WriteHeader(http.StatusNoContent) + }) + + err := projectendpoints.Create(client.ServiceClient(), "project-id", "endpoint-id").Err + th.AssertNoErr(t, err) +} + +func TestListEndpoints(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/OS-EP-FILTER/projects/project-id/endpoints", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + w.Header().Add("Content-Type", "application/json") + fmt.Fprintf(w, ` + { + "endpoints": [ + { + "id": "6fedc0", + "interface": "public", + "url": "http://example.com/identity/", + "region": "north", + "links": { + "self": "http://example.com/identity/v3/endpoints/6fedc0" + }, + "service_id": "1b501a" + }, + { + "id": "6fedc0", + "interface": "internal", + "region": "south", + "url": "http://example.com/identity/", + "links": { + "self": "http://example.com/identity/v3/endpoints/6fedc0" + }, + "service_id": "1b501a" + } + ], + "links": { + "self": "http://example.com/identity/v3/OS-EP-FILTER/projects/263fd9/endpoints", + "previous": null, + "next": null + } + } + `) + }) + + count := 0 + projectendpoints.List(client.ServiceClient(), "project-id").EachPage(func(page pagination.Page) (bool, error) { + count++ + actual, err := projectendpoints.ExtractEndpoints(page) + if err != nil { + t.Errorf("Failed to extract endpoints: %v", err) + return false, err + } + + expected := []projectendpoints.Endpoint{ + { + ID: "6fedc0", + Availability: gophercloud.AvailabilityPublic, + Region: "north", + ServiceID: "1b501a", + URL: "http://example.com/identity/", + }, + { + ID: "6fedc0", + Availability: gophercloud.AvailabilityInternal, + Region: "south", + ServiceID: "1b501a", + URL: "http://example.com/identity/", + }, + } + th.AssertDeepEquals(t, expected, actual) + return true, nil + }) + th.AssertEquals(t, 1, count) +} + +func TestDeleteEndpoint(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/OS-EP-FILTER/projects/project-id/endpoints/endpoint-id", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "DELETE") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + w.WriteHeader(http.StatusNoContent) + }) + + res := projectendpoints.Delete(client.ServiceClient(), "project-id", "endpoint-id") + th.AssertNoErr(t, res.Err) +} diff --git a/openstack/identity/v3/extensions/projectendpoints/urls.go b/openstack/identity/v3/extensions/projectendpoints/urls.go new file mode 100644 index 0000000000..d72f3f5198 --- /dev/null +++ b/openstack/identity/v3/extensions/projectendpoints/urls.go @@ -0,0 +1,15 @@ +package projectendpoints + +import "github.com/gophercloud/gophercloud" + +func listURL(client *gophercloud.ServiceClient, projectID string) string { + return client.ServiceURL("OS-EP-FILTER", "projects", projectID, "endpoints") +} + +func createURL(client *gophercloud.ServiceClient, projectID, endpointID string) string { + return client.ServiceURL("OS-EP-FILTER", "projects", projectID, "endpoints", endpointID) +} + +func deleteURL(client *gophercloud.ServiceClient, projectID, endpointID string) string { + return client.ServiceURL("OS-EP-FILTER", "projects", projectID, "endpoints", endpointID) +} From ee6687ce72afe29108934f7999ec979f998910a6 Mon Sep 17 00:00:00 2001 From: Vyacheslav Vershinin <vershinin.vyacheslav@gmail.com> Date: Fri, 7 Jan 2022 16:34:25 +0200 Subject: [PATCH 020/360] Add Missing Neutron v2 Protocol Constant "any" Add option "Any" in security group rules, according the [documentaion](https://docs.openstack.org/python-openstackclient/latest/cli/command-objects/security-group-rule.html#cmdoption-openstack-security-group-rule-create-protocol): ```bash Network version 2: IP protocol (ah, dccp, egp, esp, gre, icmp, igmp, ipv6-encap, ipv6-frag, ipv6-icmp, ipv6-nonxt, ipv6-opts, ipv6-route, ospf, pgm, rsvp, sctp, tcp, udp, udplite, vrrp and integer representations [0-255] or any; default: any (all protocols)) ``` --- openstack/networking/v2/extensions/security/rules/requests.go | 1 + 1 file changed, 1 insertion(+) diff --git a/openstack/networking/v2/extensions/security/rules/requests.go b/openstack/networking/v2/extensions/security/rules/requests.go index 544c24d7f3..4a7d5d1c18 100644 --- a/openstack/networking/v2/extensions/security/rules/requests.go +++ b/openstack/networking/v2/extensions/security/rules/requests.go @@ -74,6 +74,7 @@ const ( ProtocolUDP RuleProtocol = "udp" ProtocolUDPLite RuleProtocol = "udplite" ProtocolVRRP RuleProtocol = "vrrp" + ProtocolANY RuleProtocol = "any" ) // CreateOptsBuilder allows extensions to add additional parameters to the From 8fef9e4e5f61c3297e1f2adcaebc4922420eebe3 Mon Sep 17 00:00:00 2001 From: Emilien Macchi <emilien@redhat.com> Date: Thu, 6 Jan 2022 13:59:07 -0500 Subject: [PATCH 021/360] [CI-v2] Add functional-basic job This runs a Github Action to: * Deploy Devstack with defaults (minimum services) * Run acceptance/openstack which is a minimal set of tests * Post logs if we encounter a failure Note: run the job every day, so if something breaks in devstack we'll know sooner. --- .github/workflows/functional-basic.yaml | 59 +++++++++++++++++++++++++ script/acceptancetest | 10 +++-- script/collectlogs | 11 +++++ script/stackenv | 3 +- 4 files changed, 79 insertions(+), 4 deletions(-) create mode 100644 .github/workflows/functional-basic.yaml create mode 100755 script/collectlogs diff --git a/.github/workflows/functional-basic.yaml b/.github/workflows/functional-basic.yaml new file mode 100644 index 0000000000..dadd3bdc89 --- /dev/null +++ b/.github/workflows/functional-basic.yaml @@ -0,0 +1,59 @@ +name: functional-basic +on: + pull_request: + paths-ignore: + - '^docs/|\.md$|^(?:.*/)?(?:\.gitignore|LICENSE)$' + schedule: + - cron: '0 0 * * *' +jobs: + functional-basic: + strategy: + fail-fast: false + matrix: + name: ["master"] + openstack_version: ["master"] + ubuntu_version: ["20.04"] + include: + - name: "xena" + openstack_version: "stable/xena" + ubuntu_version: "20.04" + - name: "wallaby" + openstack_version: "stable/wallaby" + ubuntu_version: "20.04" + - name: "victoria" + openstack_version: "stable/victoria" + ubuntu_version: "20.04" + - name: "ussuri" + openstack_version: "stable/ussuri" + ubuntu_version: "18.04" + - name: "train" + openstack_version: "stable/train" + ubuntu_version: "18.04" + runs-on: ubuntu-${{ matrix.ubuntu_version }} + name: Deploy OpenStack ${{ matrix.name }} with defaults and run basic acceptance tests + steps: + - name: Checkout Gophercloud + uses: actions/checkout@v2 + - name: Deploy devstack + uses: EmilienM/devstack-action@v0.6 + with: + branch: ${{ matrix.openstack_version }} + enabled_services: 's-account,s-container,s-object,s-proxy' + - name: Checkout go + uses: actions/setup-go@v2 + with: + go-version: '^1.15' + - name: Run Gophercloud acceptance tests + run: ./script/acceptancetest + env: + DEVSTACK_PATH: ${{ github.workspace }}/devstack + ACCEPTANCE_TESTS_FILTER: '^acceptance/openstack$' + - name: Generate logs on failure + run: ./script/collectlogs + if: failure() + - name: Upload logs artifacts on failure + if: failure() + uses: actions/upload-artifact@v2 + with: + name: functional-basic-${{ matrix.name }} + path: /tmp/devstack-logs/* diff --git a/script/acceptancetest b/script/acceptancetest index e782ff20bc..d859e1b823 100755 --- a/script/acceptancetest +++ b/script/acceptancetest @@ -7,15 +7,19 @@ source `dirname $0`/stackenv timeout="60m" failed= +if [[ -z "${ACCEPTANCE_TESTS_FILTER}" ]]; then + ACCEPTANCE_TESTS=($(python <<< "print(' '.join($ACCEPTANCE_TESTS))")) +else + ACCEPTANCE_TESTS=$(find acceptance/openstack -name '*_test.go' -exec dirname {} \; | sort -n | uniq | grep -P "$ACCEPTANCE_TESTS_FILTER") + ACCEPTANCE_TESTS=($ACCEPTANCE_TESTS) +fi if [[ -z $ACCEPTANCE_TESTS ]]; then echo "No acceptance tests to run" exit 0 fi -TESTS=($(python <<< "print(' '.join($ACCEPTANCE_TESTS))")) - -for acceptance_test in "${TESTS[@]}"; do +for acceptance_test in "${ACCEPTANCE_TESTS[@]}"; do go test -v -timeout $timeout -tags "fixtures acceptance" ./${acceptance_test} # Check the error code after each suite, but do not exit early if a suite failed. if [[ $? != 0 ]]; then diff --git a/script/collectlogs b/script/collectlogs new file mode 100755 index 0000000000..8222d54d7e --- /dev/null +++ b/script/collectlogs @@ -0,0 +1,11 @@ +#!/bin/bash +set -x + +LOG_DIR=${LOG_DIR:-/tmp/devstack-logs} +mkdir -p $LOG_DIR +sudo journalctl -o short-precise --no-pager &> $LOG_DIR/journal.log +free -m > $LOG_DIR/free.txt +dpkg -l > $LOG_DIR/dpkg-l.txt +pip freeze > $LOG_DIR/pip-freeze.txt +sudo find $LOG_DIR -type d -exec chmod 0755 {} \; +sudo find $LOG_DIR -type f -exec chmod 0644 {} \; diff --git a/script/stackenv b/script/stackenv index a65e29c677..fef97cd87c 100644 --- a/script/stackenv +++ b/script/stackenv @@ -2,7 +2,8 @@ # environment variables. This env is for theopenlab CI jobs, you might need # to modify this according to your setup -pushd /opt/stack/new/devstack +DEVSTACK_PATH=${DEVSTACK_PATH:-/opt/stack/new/devstack} +pushd $DEVSTACK_PATH source openrc admin admin openstack flavor create m1.acctest --id 99 --ram 512 --disk 5 --vcpu 1 --ephemeral 10 openstack flavor create m1.resize --id 98 --ram 512 --disk 6 --vcpu 1 --ephemeral 10 From 7d3e6172c6207513b8355d384aad6f3910e9067e Mon Sep 17 00:00:00 2001 From: Vyacheslav Vershinin <vershinin.vyacheslav@gmail.com> Date: Sat, 8 Jan 2022 12:08:44 +0200 Subject: [PATCH 022/360] Update requests.go --- openstack/networking/v2/extensions/security/rules/requests.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openstack/networking/v2/extensions/security/rules/requests.go b/openstack/networking/v2/extensions/security/rules/requests.go index 4a7d5d1c18..7b5331b6d0 100644 --- a/openstack/networking/v2/extensions/security/rules/requests.go +++ b/openstack/networking/v2/extensions/security/rules/requests.go @@ -74,7 +74,7 @@ const ( ProtocolUDP RuleProtocol = "udp" ProtocolUDPLite RuleProtocol = "udplite" ProtocolVRRP RuleProtocol = "vrrp" - ProtocolANY RuleProtocol = "any" + ProtocolAny RuleProtocol = "any" ) // CreateOptsBuilder allows extensions to add additional parameters to the From 1ebdde026ab38be469d10fa986e7f7521f5a6c8b Mon Sep 17 00:00:00 2001 From: Emilien Macchi <emilien@redhat.com> Date: Fri, 7 Jan 2022 21:22:53 -0500 Subject: [PATCH 023/360] acceptance: store results into a log file This log file will be collected by Github Actions artifacts. --- script/acceptancetest | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/script/acceptancetest b/script/acceptancetest index d859e1b823..e91fe91bd3 100755 --- a/script/acceptancetest +++ b/script/acceptancetest @@ -7,6 +7,12 @@ source `dirname $0`/stackenv timeout="60m" failed= +if [[ -z "${LOG_DIR}" ]]; then + echo "LOG_DIR not set, will set a temp directory" + LOG_DIR=/tmp/devstack-logs +fi +mkdir -p ${LOG_DIR} + if [[ -z "${ACCEPTANCE_TESTS_FILTER}" ]]; then ACCEPTANCE_TESTS=($(python <<< "print(' '.join($ACCEPTANCE_TESTS))")) else @@ -20,7 +26,7 @@ if [[ -z $ACCEPTANCE_TESTS ]]; then fi for acceptance_test in "${ACCEPTANCE_TESTS[@]}"; do - go test -v -timeout $timeout -tags "fixtures acceptance" ./${acceptance_test} + go test -v -timeout $timeout -tags "fixtures acceptance" ./${acceptance_test} |& tee -a ${LOG_DIR}/acceptance_tests.log # Check the error code after each suite, but do not exit early if a suite failed. if [[ $? != 0 ]]; then failed=1 From 7db2c4ac3b66041528a94f844e9cf2b6fdc010ea Mon Sep 17 00:00:00 2001 From: Ludovic Lamarche <ludovic.lamarche@ovhcloud.com> Date: Tue, 11 Jan 2022 10:30:11 +0000 Subject: [PATCH 024/360] projectendpoints acceptance tests --- .../identity/v3/projectendpoint_test.go | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 acceptance/openstack/identity/v3/projectendpoint_test.go diff --git a/acceptance/openstack/identity/v3/projectendpoint_test.go b/acceptance/openstack/identity/v3/projectendpoint_test.go new file mode 100644 index 0000000000..e8c5770a72 --- /dev/null +++ b/acceptance/openstack/identity/v3/projectendpoint_test.go @@ -0,0 +1,56 @@ +//go:build acceptance +// +build acceptance + +package v3 + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/identity/v3/endpoints" + "github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/projectendpoints" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestProjectEndpoints(t *testing.T) { + clients.RequireAdmin(t) + + client, err := clients.NewIdentityV3Client() + th.AssertNoErr(t, err) + + // Create a project to assign endpoints. + project, err := CreateProject(t, client, nil) + th.AssertNoErr(t, err) + defer DeleteProject(t, client, project.ID) + + tools.PrintResource(t, project) + + // Get an endpoint + allEndpointsPages, err := endpoints.List(client, nil).AllPages() + th.AssertNoErr(t, err) + + allEndpoints, err := endpoints.ExtractEndpoints(allEndpointsPages) + th.AssertNoErr(t, err) + th.AssertIntGreaterOrEqual(t, len(allEndpoints), 1) + endpoint := allEndpoints[0] + + // Attach endpoint + err = projectendpoints.Create(client, project.ID, endpoint.ID).Err + th.AssertNoErr(t, err) + + // List endpoints + allProjectEndpointsPages, err := projectendpoints.List(client, project.ID).AllPages() + th.AssertNoErr(t, err) + + allProjectEndpoints, err := projectendpoints.ExtractEndpoints(allProjectEndpointsPages) + th.AssertNoErr(t, err) + th.AssertEquals(t, 1, len(allProjectEndpoints)) + + tools.PrintResource(t, allProjectEndpoints[0]) + + // Detach endpoint + err = projectendpoints.Delete(client, project.ID, endpoint.ID).Err + th.AssertNoErr(t, err) + +} From 6753f3b619e99b5de7d73900ff5af3e34cdd3d43 Mon Sep 17 00:00:00 2001 From: Emilien Macchi <emilien@redhat.com> Date: Tue, 11 Jan 2022 17:04:09 -0500 Subject: [PATCH 025/360] acceptance: add pipefail to `acceptancetest` script Since we now redirect the output to a log file, we need to enable pipefail otherwise RC will always be 0 which is wrong if tests fail. --- script/acceptancetest | 1 + 1 file changed, 1 insertion(+) diff --git a/script/acceptancetest b/script/acceptancetest index e91fe91bd3..09db3f38fa 100755 --- a/script/acceptancetest +++ b/script/acceptancetest @@ -1,6 +1,7 @@ #!/bin/bash # set -x +set -o pipefail source `dirname $0`/stackenv From 675973d3309d0b08824ce858be2ee6841aae8cd9 Mon Sep 17 00:00:00 2001 From: Emilien Macchi <emilien@redhat.com> Date: Sun, 9 Jan 2022 09:25:43 -0500 Subject: [PATCH 026/360] [CI-v2] Add compute job with Github Action This runs a Github Action to: * Deploy Devstack with Nova and Cinder * Run acceptance/openstack/compute * Post logs if we encounter a failure * Fix aggregate test to a hostname instead of the FQDN. Nova takes hostname: https://github.com/openstack/nova/blob/master/nova/compute/api.py#L6454 * Skip UsageTest and will fix later. The test doesn't pass for now, the usage is null everywhere we need to figure out why. * Switch QuotaSet tests to use API v3 of Keystone instead of v2, which was removed in previous versions of OpenStack. * Unskip all tests, so we test everything :-) --- .github/workflows/functional-compute.yaml | 61 ++++++++++++++++ .../openstack/compute/v2/aggregates_test.go | 15 ++-- .../compute/v2/instance_actions_test.go | 6 -- .../openstack/compute/v2/keypairs_test.go | 3 - .../openstack/compute/v2/migrate_test.go | 2 - .../openstack/compute/v2/quotaset_test.go | 70 +++++++------------ .../openstack/compute/v2/servergroup_test.go | 6 -- .../openstack/compute/v2/servers_test.go | 8 --- .../openstack/compute/v2/services_test.go | 1 - acceptance/openstack/compute/v2/usage_test.go | 3 +- 10 files changed, 96 insertions(+), 79 deletions(-) create mode 100644 .github/workflows/functional-compute.yaml diff --git a/.github/workflows/functional-compute.yaml b/.github/workflows/functional-compute.yaml new file mode 100644 index 0000000000..fb4f2ed78d --- /dev/null +++ b/.github/workflows/functional-compute.yaml @@ -0,0 +1,61 @@ +name: functional-compute +on: + pull_request: + paths: + - '^.*compute.*$' + - '.github/workflows/functional-compute.yaml' + schedule: + - cron: '0 0 * * *' +jobs: + functional-compute: + strategy: + fail-fast: false + matrix: + name: ["master"] + openstack_version: ["master"] + ubuntu_version: ["20.04"] + include: + - name: "xena" + openstack_version: "stable/xena" + ubuntu_version: "20.04" + - name: "wallaby" + openstack_version: "stable/wallaby" + ubuntu_version: "20.04" + - name: "victoria" + openstack_version: "stable/victoria" + ubuntu_version: "20.04" + - name: "ussuri" + openstack_version: "stable/ussuri" + ubuntu_version: "18.04" + - name: "train" + openstack_version: "stable/train" + ubuntu_version: "18.04" + runs-on: ubuntu-${{ matrix.ubuntu_version }} + name: Deploy OpenStack ${{ matrix.name }} with Nova and run compute acceptance tests + steps: + - name: Checkout Gophercloud + uses: actions/checkout@v2 + - name: Deploy devstack + uses: EmilienM/devstack-action@v0.6 + with: + branch: ${{ matrix.openstack_version }} + conf_overrides: | + CINDER_ISCSI_HELPER=tgtadm + - name: Checkout go + uses: actions/setup-go@v2 + with: + go-version: '^1.15' + - name: Run Gophercloud acceptance tests + run: ./script/acceptancetest + env: + DEVSTACK_PATH: ${{ github.workspace }}/devstack + ACCEPTANCE_TESTS_FILTER: "^.*compute.*$" + - name: Generate logs on failure + run: ./script/collectlogs + if: failure() + - name: Upload logs artifacts on failure + if: failure() + uses: actions/upload-artifact@v2 + with: + name: functional-compute-${{ matrix.name }} + path: /tmp/devstack-logs/* diff --git a/acceptance/openstack/compute/v2/aggregates_test.go b/acceptance/openstack/compute/v2/aggregates_test.go index 208ad3b1ad..a90a77b8dd 100644 --- a/acceptance/openstack/compute/v2/aggregates_test.go +++ b/acceptance/openstack/compute/v2/aggregates_test.go @@ -5,6 +5,7 @@ package v2 import ( "fmt" + "strings" "testing" "github.com/gophercloud/gophercloud" @@ -73,7 +74,7 @@ func TestAggregatesAddRemoveHost(t *testing.T) { defer DeleteAggregate(t, client, aggregate) addHostOpts := aggregates.AddHostOpts{ - Host: hostToAdd.HypervisorHostname, + Host: hostToAdd, } aggregateWithNewHost, err := aggregates.AddHost(client, aggregate.ID, addHostOpts).Extract() @@ -81,10 +82,10 @@ func TestAggregatesAddRemoveHost(t *testing.T) { tools.PrintResource(t, aggregateWithNewHost) - th.AssertEquals(t, aggregateWithNewHost.Hosts[0], hostToAdd.HypervisorHostname) + th.AssertEquals(t, aggregateWithNewHost.Hosts[0], hostToAdd) removeHostOpts := aggregates.RemoveHostOpts{ - Host: hostToAdd.HypervisorHostname, + Host: hostToAdd, } aggregateWithRemovedHost, err := aggregates.RemoveHost(client, aggregate.ID, removeHostOpts).Extract() @@ -132,7 +133,7 @@ func TestAggregatesSetRemoveMetadata(t *testing.T) { } } -func getHypervisor(t *testing.T, client *gophercloud.ServiceClient) (*hypervisors.Hypervisor, error) { +func getHypervisor(t *testing.T, client *gophercloud.ServiceClient) (string, error) { allPages, err := hypervisors.List(client, nil).AllPages() th.AssertNoErr(t, err) @@ -140,8 +141,10 @@ func getHypervisor(t *testing.T, client *gophercloud.ServiceClient) (*hypervisor th.AssertNoErr(t, err) for _, h := range allHypervisors { - return &h, nil + // Nova API takes Hostnames, not FQDNs, so we need to strip the domain. + host := strings.Split(h.HypervisorHostname, ".")[0] + return host, nil } - return nil, fmt.Errorf("Unable to get hypervisor") + return "", fmt.Errorf("Unable to get hypervisor") } diff --git a/acceptance/openstack/compute/v2/instance_actions_test.go b/acceptance/openstack/compute/v2/instance_actions_test.go index e2f861ad91..3b60d90067 100644 --- a/acceptance/openstack/compute/v2/instance_actions_test.go +++ b/acceptance/openstack/compute/v2/instance_actions_test.go @@ -44,12 +44,6 @@ func TestInstanceActions(t *testing.T) { func TestInstanceActionsMicroversions(t *testing.T) { clients.RequireLong(t) - clients.SkipRelease(t, "stable/mitaka") - clients.SkipRelease(t, "stable/newton") - clients.SkipRelease(t, "stable/ocata") - clients.SkipRelease(t, "stable/pike") - clients.SkipRelease(t, "stable/queens") - clients.SkipRelease(t, "stable/rocky") now := time.Now() diff --git a/acceptance/openstack/compute/v2/keypairs_test.go b/acceptance/openstack/compute/v2/keypairs_test.go index f057191633..dd6c577963 100644 --- a/acceptance/openstack/compute/v2/keypairs_test.go +++ b/acceptance/openstack/compute/v2/keypairs_test.go @@ -18,9 +18,6 @@ import ( const keyName = "gophercloud_test_key_pair" func TestKeyPairsParse(t *testing.T) { - clients.SkipRelease(t, "stable/mitaka") - clients.SkipRelease(t, "stable/newton") - client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) diff --git a/acceptance/openstack/compute/v2/migrate_test.go b/acceptance/openstack/compute/v2/migrate_test.go index a5b99ee318..fcc69e7fb9 100644 --- a/acceptance/openstack/compute/v2/migrate_test.go +++ b/acceptance/openstack/compute/v2/migrate_test.go @@ -12,8 +12,6 @@ import ( ) func TestMigrate(t *testing.T) { - t.Skip("This is not passing in OpenLab. Works locally") - clients.RequireLong(t) clients.RequireAdmin(t) diff --git a/acceptance/openstack/compute/v2/quotaset_test.go b/acceptance/openstack/compute/v2/quotaset_test.go index 1341207fa7..3273b81b22 100644 --- a/acceptance/openstack/compute/v2/quotaset_test.go +++ b/acceptance/openstack/compute/v2/quotaset_test.go @@ -12,32 +12,21 @@ import ( "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/quotasets" - "github.com/gophercloud/gophercloud/openstack/identity/v2/tenants" + "github.com/gophercloud/gophercloud/openstack/identity/v3/projects" th "github.com/gophercloud/gophercloud/testhelper" ) func TestQuotasetGet(t *testing.T) { - clients.SkipRelease(t, "master") - clients.SkipRelease(t, "stable/mitaka") - clients.SkipRelease(t, "stable/newton") - clients.SkipRelease(t, "stable/ocata") - clients.SkipRelease(t, "stable/pike") - clients.SkipRelease(t, "stable/queens") - clients.SkipRelease(t, "stable/rocky") - clients.SkipRelease(t, "stable/stein") - clients.SkipRelease(t, "stable/train") - clients.SkipRelease(t, "stable/ussuri") - client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) - identityClient, err := clients.NewIdentityV2Client() + identityClient, err := clients.NewIdentityV3Client() th.AssertNoErr(t, err) - tenantID, err := getTenantID(t, identityClient) + projectID, err := getProjectID(t, identityClient) th.AssertNoErr(t, err) - quotaSet, err := quotasets.Get(client, tenantID).Extract() + quotaSet, err := quotasets.Get(client, projectID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, quotaSet) @@ -45,34 +34,34 @@ func TestQuotasetGet(t *testing.T) { th.AssertEquals(t, quotaSet.FixedIPs, -1) } -func getTenantID(t *testing.T, client *gophercloud.ServiceClient) (string, error) { - allPages, err := tenants.List(client, nil).AllPages() +func getProjectID(t *testing.T, client *gophercloud.ServiceClient) (string, error) { + allPages, err := projects.ListAvailable(client).AllPages() th.AssertNoErr(t, err) - allTenants, err := tenants.ExtractTenants(allPages) + allProjects, err := projects.ExtractProjects(allPages) th.AssertNoErr(t, err) - for _, tenant := range allTenants { - return tenant.ID, nil + for _, project := range allProjects { + return project.ID, nil } - return "", fmt.Errorf("Unable to get tenant ID") + return "", fmt.Errorf("Unable to get project ID") } -func getTenantIDByName(t *testing.T, client *gophercloud.ServiceClient, name string) (string, error) { - allPages, err := tenants.List(client, nil).AllPages() +func getProjectIDByName(t *testing.T, client *gophercloud.ServiceClient, name string) (string, error) { + allPages, err := projects.List(client, nil).AllPages() th.AssertNoErr(t, err) - allTenants, err := tenants.ExtractTenants(allPages) + allProjects, err := projects.ExtractProjects(allPages) th.AssertNoErr(t, err) - for _, tenant := range allTenants { - if tenant.Name == name { - return tenant.ID, nil + for _, project := range allProjects { + if project.Name == name { + return project.ID, nil } } - return "", fmt.Errorf("Unable to get tenant ID") + return "", fmt.Errorf("Unable to get project ID") } // What will be sent as desired Quotas to the Server @@ -112,43 +101,32 @@ var UpdatedQuotas = quotasets.QuotaSet{ } func TestQuotasetUpdateDelete(t *testing.T) { - clients.SkipRelease(t, "master") - clients.SkipRelease(t, "stable/mitaka") - clients.SkipRelease(t, "stable/newton") - clients.SkipRelease(t, "stable/ocata") - clients.SkipRelease(t, "stable/pike") - clients.SkipRelease(t, "stable/queens") - clients.SkipRelease(t, "stable/rocky") - clients.SkipRelease(t, "stable/stein") - clients.SkipRelease(t, "stable/train") - clients.SkipRelease(t, "stable/ussuri") - clients.RequireAdmin(t) client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) - idclient, err := clients.NewIdentityV2Client() + idclient, err := clients.NewIdentityV3Client() th.AssertNoErr(t, err) - tenantid, err := getTenantIDByName(t, idclient, os.Getenv("OS_TENANT_NAME")) + projectid, err := getProjectIDByName(t, idclient, os.Getenv("OS_PROJECT_NAME")) th.AssertNoErr(t, err) // save original quotas - orig, err := quotasets.Get(client, tenantid).Extract() + orig, err := quotasets.Get(client, projectid).Extract() th.AssertNoErr(t, err) // Test Update - res, err := quotasets.Update(client, tenantid, UpdateQuotaOpts).Extract() + res, err := quotasets.Update(client, projectid, UpdateQuotaOpts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, UpdatedQuotas, *res) // Test Delete - _, err = quotasets.Delete(client, tenantid).Extract() + _, err = quotasets.Delete(client, projectid).Extract() th.AssertNoErr(t, err) // We dont know the default quotas, so just check if the quotas are not the same as before - newres, err := quotasets.Get(client, tenantid).Extract() + newres, err := quotasets.Get(client, projectid).Extract() th.AssertNoErr(t, err) if newres.RAM == res.RAM { t.Fatalf("Failed to update quotas") @@ -158,7 +136,7 @@ func TestQuotasetUpdateDelete(t *testing.T) { FillUpdateOptsFromQuotaSet(*orig, &restore) // restore original quotas - res, err = quotasets.Update(client, tenantid, restore).Extract() + res, err = quotasets.Update(client, projectid, restore).Extract() th.AssertNoErr(t, err) orig.ID = "" diff --git a/acceptance/openstack/compute/v2/servergroup_test.go b/acceptance/openstack/compute/v2/servergroup_test.go index ee9b89271b..fe11c28a3f 100644 --- a/acceptance/openstack/compute/v2/servergroup_test.go +++ b/acceptance/openstack/compute/v2/servergroup_test.go @@ -72,12 +72,6 @@ func TestServergroupsAffinityPolicy(t *testing.T) { } func TestServergroupsMicroversionCreateDelete(t *testing.T) { - clients.SkipRelease(t, "stable/mitaka") - clients.SkipRelease(t, "stable/newton") - clients.SkipRelease(t, "stable/ocata") - clients.SkipRelease(t, "stable/pike") - clients.SkipRelease(t, "stable/queens") - client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) diff --git a/acceptance/openstack/compute/v2/servers_test.go b/acceptance/openstack/compute/v2/servers_test.go index d65b69654d..953cb05720 100644 --- a/acceptance/openstack/compute/v2/servers_test.go +++ b/acceptance/openstack/compute/v2/servers_test.go @@ -87,8 +87,6 @@ func TestServersCreateDestroy(t *testing.T) { } func TestServersWithExtensionsCreateDestroy(t *testing.T) { - clients.SkipRelease(t, "stable/mitaka") - clients.RequireLong(t) var extendedServer struct { @@ -491,9 +489,6 @@ func TestServersConsoleOutput(t *testing.T) { func TestServersTags(t *testing.T) { clients.RequireLong(t) - clients.SkipRelease(t, "stable/mitaka") - clients.SkipRelease(t, "stable/newton") - clients.SkipRelease(t, "stable/ocata") choices, err := clients.AcceptanceTestChoicesFromEnv() th.AssertNoErr(t, err) @@ -567,9 +562,6 @@ func TestServersTags(t *testing.T) { func TestServersWithExtendedAttributesCreateDestroy(t *testing.T) { clients.RequireLong(t) clients.RequireAdmin(t) - clients.SkipRelease(t, "stable/mitaka") - clients.SkipRelease(t, "stable/newton") - clients.SkipRelease(t, "stable/ocata") client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) diff --git a/acceptance/openstack/compute/v2/services_test.go b/acceptance/openstack/compute/v2/services_test.go index 6dc590eeee..d7d36d7f2c 100644 --- a/acceptance/openstack/compute/v2/services_test.go +++ b/acceptance/openstack/compute/v2/services_test.go @@ -66,7 +66,6 @@ func TestServicesListWithOpts(t *testing.T) { } func TestServicesUpdate(t *testing.T) { - clients.SkipRelease(t, "stable/ocata") clients.RequireAdmin(t) client, err := clients.NewComputeV2Client() diff --git a/acceptance/openstack/compute/v2/usage_test.go b/acceptance/openstack/compute/v2/usage_test.go index 9f73eb9e85..c4874a68c6 100644 --- a/acceptance/openstack/compute/v2/usage_test.go +++ b/acceptance/openstack/compute/v2/usage_test.go @@ -16,7 +16,8 @@ import ( ) func TestUsageSingleTenant(t *testing.T) { - t.Skip("This is not passing in OpenLab. Works locally") + // TODO(emilien): This test is failing for now + t.Skip("This is not passing now, will fix later") clients.RequireLong(t) From 87d87f45beaf6ac75cea81393085a29178943c51 Mon Sep 17 00:00:00 2001 From: Emilien Macchi <emilien@redhat.com> Date: Thu, 6 Jan 2022 14:17:06 -0500 Subject: [PATCH 027/360] [CI-v2] Replace baremetal job with Github Action This runs a Github Action to: * Deploy Devstack with Ironic * Add a function to skip tests below a certain OpenStack release * Skip TestNodesRAIDConfig below Ussuri, it's not working anyway. * Run acceptance/openstack/baremetal * Post logs if we encounter a failure * Replace the previous CI job managed in Zuul --- .github/workflows/functional-baremetal.yaml | 93 +++++++++++++++++++ .zuul.yaml | 16 ---- acceptance/clients/conditions.go | 10 ++ .../baremetal/httpbasic/nodes_test.go | 1 + .../openstack/baremetal/noauth/nodes_test.go | 1 + .../openstack/baremetal/v1/nodes_test.go | 1 + 6 files changed, 106 insertions(+), 16 deletions(-) create mode 100644 .github/workflows/functional-baremetal.yaml diff --git a/.github/workflows/functional-baremetal.yaml b/.github/workflows/functional-baremetal.yaml new file mode 100644 index 0000000000..3310571b90 --- /dev/null +++ b/.github/workflows/functional-baremetal.yaml @@ -0,0 +1,93 @@ +name: functional-baremetal +on: + pull_request: + paths: + - '^.*baremetal.*$' + - '.github/workflows/functional-baremetal.yaml' + schedule: + - cron: '0 0 * * *' +jobs: + functional-baremetal: + strategy: + fail-fast: false + matrix: + name: ["master"] + openstack_version: ["master"] + ubuntu_version: ["20.04"] + include: + - name: "xena" + openstack_version: "stable/xena" + ubuntu_version: "20.04" + - name: "wallaby" + openstack_version: "stable/wallaby" + ubuntu_version: "20.04" + - name: "victoria" + openstack_version: "stable/victoria" + ubuntu_version: "20.04" + - name: "ussuri" + openstack_version: "stable/ussuri" + ubuntu_version: "18.04" + - name: "train" + openstack_version: "stable/train" + ubuntu_version: "18.04" + runs-on: ubuntu-${{ matrix.ubuntu_version }} + name: Deploy OpenStack ${{ matrix.name }} with Ironic and run baremetal acceptance tests + steps: + - name: Checkout Gophercloud + uses: actions/checkout@v2 + - name: Deploy devstack + uses: EmilienM/devstack-action@v0.6 + with: + branch: ${{ matrix.openstack_version }} + conf_overrides: | + enable_plugin ironic https://opendev.org/openstack/ironic ${{ matrix.openstack_version }} + LIBS_FROM_GIT=pyghmi,virtualbmc + FORCE_CONFIG_DRIVE=True + Q_AGENT=openvswitch + Q_ML2_TENANT_NETWORK_TYPE=vxlan + Q_ML2_PLUGIN_MECHANISM_DRIVERS=openvswitch + DEFAULT_INSTANCE_TYPE=baremetal + OVERRIDE_PUBLIC_BRIDGE_MTU=1400 + VIRT_DRIVER=ironic + BUILD_TIMEOUT=1800 + SERVICE_TIMEOUT=90 + GLANCE_LIMIT_IMAGE_SIZE_TOTAL=5000 + Q_USE_SECGROUP=False + API_WORKERS=1 + IRONIC_BAREMETAL_BASIC_OPS=True + IRONIC_BUILD_DEPLOY_RAMDISK=False + IRONIC_AUTOMATED_CLEAN_ENABLED=False + IRONIC_CALLBACK_TIMEOUT=600 + IRONIC_DEPLOY_DRIVER=ipmi + IRONIC_INSPECTOR_BUILD_RAMDISK=False + IRONIC_RAMDISK_TYPE=tinyipa + IRONIC_TEMPEST_BUILD_TIMEOUT=720 + IRONIC_TEMPEST_WHOLE_DISK_IMAGE=False + IRONIC_VM_COUNT=1 + IRONIC_VM_EPHEMERAL_DISK=1 + IRONIC_VM_LOG_DIR=/opt/stack/new/ironic-bm-logs + IRONIC_VM_SPECS_RAM=1024 + IRONIC_DEFAULT_DEPLOY_INTERFACE=direct + IRONIC_ENABLED_DEPLOY_INTERFACES=direct + SWIFT_ENABLE_TEMPURLS=True + SWIFT_TEMPURL_KEY=secretkey + enabled_services: 'ir-api,ir-cond,s-account,s-container,s-object,s-proxy,q-svc,q-agt,q-dhcp,q-l3,q-meta,-cinder,-c-sch,-c-api,-c-vol,-c-bak,-ovn,-ovn-controller,-ovn-northd,-q-ovn-metadata-agent' + - name: Checkout go + uses: actions/setup-go@v2 + with: + go-version: '^1.15' + - name: Run Gophercloud acceptance tests + run: ./script/acceptancetest + env: + DEVSTACK_PATH: ${{ github.workspace }}/devstack + ACCEPTANCE_TESTS_FILTER: '^.*baremetal(.(?!noauth).*)?$' + OS_BRANCH: ${{ matrix.openstack_version }} + - name: Generate logs on failure + run: ./script/collectlogs + if: failure() + - name: Upload logs artifacts on failure + if: failure() + uses: actions/upload-artifact@v2 + with: + name: functional-baremetal-${{ matrix.name }} + path: /tmp/devstack-logs/* diff --git a/.zuul.yaml b/.zuul.yaml index 1f7ed53bc3..ab32778978 100644 --- a/.zuul.yaml +++ b/.zuul.yaml @@ -105,21 +105,6 @@ devstack_services: - manila -- job: - name: gophercloud-acceptance-test-ironic - parent: gophercloud-acceptance-test-base - description: | - Run gophercloud ironic acceptance test on master branch - files: - - ^.*baremetal.*$ - vars: - devstack_services: - - ironic - devstack_override_enabled_services: 'g-api,g-reg,q-agt,q-dhcp,q-l3,q-svc,key,mysql,rabbit,ir-api,ir-cond,s-account,s-container,s-object,s-proxy' - devstack_projects: 'openstack/ironic-python-agent-builder openstack/ironic' - acceptance_tests: - - acceptance/openstack/baremetal/v1 - - job: name: gophercloud-acceptance-test-compute-ussuri parent: gophercloud-acceptance-test-compute @@ -490,7 +475,6 @@ - gophercloud-acceptance-test-compute - gophercloud-acceptance-test-networking - gophercloud-acceptance-test-storage - - gophercloud-acceptance-test-ironic recheck-newton: jobs: - gophercloud-acceptance-test-compute-newton diff --git a/acceptance/clients/conditions.go b/acceptance/clients/conditions.go index b98754edde..309cdb8cf7 100644 --- a/acceptance/clients/conditions.go +++ b/acceptance/clients/conditions.go @@ -89,3 +89,13 @@ func SkipRelease(t *testing.T, release string) { t.Skipf("this is not supported in %s", release) } } + +// SkipReleasesBelow will have the test be skipped on releases below a certain +// one. Releases are named such as 'stable/mitaka', master, etc. +func SkipReleasesBelow(t *testing.T, release string) { + current_branch := os.Getenv("OS_BRANCH") + + if current_branch != "master" && current_branch < release { + t.Skipf("this is not supported below %s, testing in %s", release, current_branch) + } +} diff --git a/acceptance/openstack/baremetal/httpbasic/nodes_test.go b/acceptance/openstack/baremetal/httpbasic/nodes_test.go index 56b87a8af7..79995912e3 100644 --- a/acceptance/openstack/baremetal/httpbasic/nodes_test.go +++ b/acceptance/openstack/baremetal/httpbasic/nodes_test.go @@ -69,6 +69,7 @@ func TestNodesUpdate(t *testing.T) { } func TestNodesRAIDConfig(t *testing.T) { + clients.SkipReleasesBelow(t, "stable/ussuri") clients.RequireLong(t) clients.RequireIronicHTTPBasic(t) diff --git a/acceptance/openstack/baremetal/noauth/nodes_test.go b/acceptance/openstack/baremetal/noauth/nodes_test.go index 8095cf59a0..09969992a3 100644 --- a/acceptance/openstack/baremetal/noauth/nodes_test.go +++ b/acceptance/openstack/baremetal/noauth/nodes_test.go @@ -67,6 +67,7 @@ func TestNodesUpdate(t *testing.T) { } func TestNodesRAIDConfig(t *testing.T) { + clients.SkipReleasesBelow(t, "stable/ussuri") clients.RequireLong(t) client, err := clients.NewBareMetalV1NoAuthClient() diff --git a/acceptance/openstack/baremetal/v1/nodes_test.go b/acceptance/openstack/baremetal/v1/nodes_test.go index 68e8b79fac..4f26e2d0cf 100644 --- a/acceptance/openstack/baremetal/v1/nodes_test.go +++ b/acceptance/openstack/baremetal/v1/nodes_test.go @@ -69,6 +69,7 @@ func TestNodesUpdate(t *testing.T) { } func TestNodesRAIDConfig(t *testing.T) { + clients.SkipReleasesBelow(t, "stable/ussuri") clients.RequireLong(t) client, err := clients.NewBareMetalV1Client() From 91d408396ab57d3b479d7dcfbaa2ef48d0f4405d Mon Sep 17 00:00:00 2001 From: Emilien Macchi <emilien@redhat.com> Date: Tue, 11 Jan 2022 10:44:39 -0500 Subject: [PATCH 028/360] [CI-v2] Add identity job with Github Action This runs a Github Action to: * Deploy Devstack with Keystone * Run acceptance/openstack/identity/v3 * Fix test for roles: we were creating a role with a random name and not taking in consideration that CreateOpts could contain a Name. Also, we were creating the role in the default domain but this is problematic because right now Gophercloud is not able to delete a role in a specific domain. This is a feature that will need to be added (issue #2321) * Post logs if we encounter a failure Note: we don't have CI coverage for v2 API because it was removed long time ago (in Pike): https://docs.openstack.org/releasenotes/keystone/pike.html#deprecation-notes --- .github/workflows/functional-identity.yaml | 59 +++++++++++++++++++ .../v3/applicationcredentials_test.go | 12 ---- acceptance/openstack/identity/v3/identity.go | 9 ++- .../openstack/identity/v3/oauth1_test.go | 4 -- .../openstack/identity/v3/projects_test.go | 3 - .../openstack/identity/v3/roles_test.go | 24 ++------ .../openstack/identity/v3/trusts_test.go | 5 -- 7 files changed, 70 insertions(+), 46 deletions(-) create mode 100644 .github/workflows/functional-identity.yaml diff --git a/.github/workflows/functional-identity.yaml b/.github/workflows/functional-identity.yaml new file mode 100644 index 0000000000..09b9715e55 --- /dev/null +++ b/.github/workflows/functional-identity.yaml @@ -0,0 +1,59 @@ +name: functional-identity +on: + pull_request: + paths: + - '^.*identity.*$' + - '.github/workflows/functional-identity.yaml' + schedule: + - cron: '0 0 * * *' +jobs: + functional-identity: + strategy: + fail-fast: false + matrix: + name: ["master"] + openstack_version: ["master"] + ubuntu_version: ["20.04"] + include: + - name: "xena" + openstack_version: "stable/xena" + ubuntu_version: "20.04" + - name: "wallaby" + openstack_version: "stable/wallaby" + ubuntu_version: "20.04" + - name: "victoria" + openstack_version: "stable/victoria" + ubuntu_version: "20.04" + - name: "ussuri" + openstack_version: "stable/ussuri" + ubuntu_version: "18.04" + - name: "train" + openstack_version: "stable/train" + ubuntu_version: "18.04" + runs-on: ubuntu-${{ matrix.ubuntu_version }} + name: Deploy OpenStack ${{ matrix.name }} with Keystone and run identity acceptance tests + steps: + - name: Checkout Gophercloud + uses: actions/checkout@v2 + - name: Deploy devstack + uses: EmilienM/devstack-action@v0.6 + with: + branch: ${{ matrix.openstack_version }} + - name: Checkout go + uses: actions/setup-go@v2 + with: + go-version: '^1.15' + - name: Run Gophercloud acceptance tests + run: ./script/acceptancetest + env: + DEVSTACK_PATH: ${{ github.workspace }}/devstack + ACCEPTANCE_TESTS_FILTER: "^.*identity.*$" + - name: Generate logs on failure + run: ./script/collectlogs + if: failure() + - name: Upload logs artifacts on failure + if: failure() + uses: actions/upload-artifact@v2 + with: + name: functional-identity-${{ matrix.name }} + path: /tmp/devstack-logs/* diff --git a/acceptance/openstack/identity/v3/applicationcredentials_test.go b/acceptance/openstack/identity/v3/applicationcredentials_test.go index e9dc615c45..e1758c5b2f 100644 --- a/acceptance/openstack/identity/v3/applicationcredentials_test.go +++ b/acceptance/openstack/identity/v3/applicationcredentials_test.go @@ -16,11 +16,6 @@ import ( ) func TestApplicationCredentialsCRD(t *testing.T) { - clients.SkipRelease(t, "stable/mitaka") - clients.SkipRelease(t, "stable/newton") - clients.SkipRelease(t, "stable/ocata") - clients.SkipRelease(t, "stable/pike") - // maps are required, because Application Credential roles are returned in a random order rolesToMap := func(roles []applicationcredentials.Role) map[string]string { rolesMap := map[string]string{} @@ -174,13 +169,6 @@ func TestApplicationCredentialsCRD(t *testing.T) { func TestApplicationCredentialsAccessRules(t *testing.T) { clients.RequireAdmin(t) - clients.SkipRelease(t, "stable/mitaka") - clients.SkipRelease(t, "stable/newton") - clients.SkipRelease(t, "stable/ocata") - clients.SkipRelease(t, "stable/pike") - clients.SkipRelease(t, "stable/queens") - clients.SkipRelease(t, "stable/rocky") - clients.SkipRelease(t, "stable/stein") client, err := clients.NewIdentityV3Client() th.AssertNoErr(t, err) diff --git a/acceptance/openstack/identity/v3/identity.go b/acceptance/openstack/identity/v3/identity.go index ade0b15804..3824b2aa0a 100644 --- a/acceptance/openstack/identity/v3/identity.go +++ b/acceptance/openstack/identity/v3/identity.go @@ -140,7 +140,13 @@ func CreateDomain(t *testing.T, client *gophercloud.ServiceClient, c *domains.Cr // has so many options. An error will be returned if the role was // unable to be created. func CreateRole(t *testing.T, client *gophercloud.ServiceClient, c *roles.CreateOpts) (*roles.Role, error) { - name := tools.RandomString("ACPTTEST", 8) + var name string + if c.Name == "" { + name = tools.RandomString("ACPTTEST", 8) + } else { + name = c.Name + } + t.Logf("Attempting to create role: %s", name) var createOpts roles.CreateOpts @@ -149,7 +155,6 @@ func CreateRole(t *testing.T, client *gophercloud.ServiceClient, c *roles.Create } else { createOpts = roles.CreateOpts{} } - createOpts.Name = name role, err := roles.Create(client, createOpts).Extract() diff --git a/acceptance/openstack/identity/v3/oauth1_test.go b/acceptance/openstack/identity/v3/oauth1_test.go index 848fd64aeb..0a8dd3c0ee 100644 --- a/acceptance/openstack/identity/v3/oauth1_test.go +++ b/acceptance/openstack/identity/v3/oauth1_test.go @@ -16,10 +16,6 @@ import ( ) func TestOAuth1CRUD(t *testing.T) { - clients.SkipRelease(t, "stable/mitaka") - clients.SkipRelease(t, "stable/newton") - clients.SkipRelease(t, "stable/ocata") - client, err := clients.NewIdentityV3Client() th.AssertNoErr(t, err) diff --git a/acceptance/openstack/identity/v3/projects_test.go b/acceptance/openstack/identity/v3/projects_test.go index a81cb2d1c1..4c23a45988 100644 --- a/acceptance/openstack/identity/v3/projects_test.go +++ b/acceptance/openstack/identity/v3/projects_test.go @@ -214,9 +214,6 @@ func TestProjectsNested(t *testing.T) { } func TestProjectsTags(t *testing.T) { - clients.SkipRelease(t, "stable/mitaka") - clients.SkipRelease(t, "stable/newton") - clients.SkipRelease(t, "stable/ocata") clients.RequireAdmin(t) client, err := clients.NewIdentityV3Client() diff --git a/acceptance/openstack/identity/v3/roles_test.go b/acceptance/openstack/identity/v3/roles_test.go index 43383e1be1..051f838d0f 100644 --- a/acceptance/openstack/identity/v3/roles_test.go +++ b/acceptance/openstack/identity/v3/roles_test.go @@ -58,8 +58,7 @@ func TestRolesCRUD(t *testing.T) { th.AssertNoErr(t, err) createOpts := roles.CreateOpts{ - Name: "testrole", - DomainID: "default", + Name: "testrole", Extra: map[string]interface{}{ "description": "test role description", }, @@ -73,9 +72,7 @@ func TestRolesCRUD(t *testing.T) { tools.PrintResource(t, role) tools.PrintResource(t, role.Extra) - listOpts := roles.ListOpts{ - DomainID: "default", - } + listOpts := roles.ListOpts{} allPages, err := roles.List(client, listOpts).AllPages() th.AssertNoErr(t, err) @@ -112,24 +109,11 @@ func TestRolesCRUD(t *testing.T) { func TestRolesFilterList(t *testing.T) { clients.RequireAdmin(t) - // For some reason this is not longer working. - clients.SkipRelease(t, "master") - clients.SkipRelease(t, "stable/mitaka") - clients.SkipRelease(t, "stable/newton") - clients.SkipRelease(t, "stable/ocata") - clients.SkipRelease(t, "stable/pike") - clients.SkipRelease(t, "stable/queens") - clients.SkipRelease(t, "stable/rocky") - clients.SkipRelease(t, "stable/stein") - clients.SkipRelease(t, "stable/train") - clients.SkipRelease(t, "stable/ussuri") - client, err := clients.NewIdentityV3Client() th.AssertNoErr(t, err) createOpts := roles.CreateOpts{ - Name: "testrole", - DomainID: "default", + Name: "testrole", Extra: map[string]interface{}{ "description": "test role description", }, @@ -142,7 +126,7 @@ func TestRolesFilterList(t *testing.T) { var listOpts roles.ListOpts listOpts.Filters = map[string]string{ - "name__contains": "TEST", + "name__contains": "test", } allPages, err := roles.List(client, listOpts).AllPages() diff --git a/acceptance/openstack/identity/v3/trusts_test.go b/acceptance/openstack/identity/v3/trusts_test.go index abc099e719..66dd42d08c 100644 --- a/acceptance/openstack/identity/v3/trusts_test.go +++ b/acceptance/openstack/identity/v3/trusts_test.go @@ -18,11 +18,6 @@ import ( ) func TestTrustCRUD(t *testing.T) { - clients.SkipRelease(t, "stable/mitaka") - clients.SkipRelease(t, "stable/newton") - clients.SkipRelease(t, "stable/ocata") - clients.SkipRelease(t, "stable/pike") - clients.SkipRelease(t, "stable/queens") clients.RequireAdmin(t) client, err := clients.NewIdentityV3Client() From 0c3879bf55372dbe72f84e67ba30b30b9e46726d Mon Sep 17 00:00:00 2001 From: Emilien Macchi <emilien@redhat.com> Date: Fri, 7 Jan 2022 20:34:33 -0500 Subject: [PATCH 029/360] [CI-v2] Replace blockstorage job with Github Action This runs a Github Action to: * Deploy Devstack with Cinder and Swift * Run acceptance/openstack/blockstorage * Post logs if we encounter a failure * Replace the previous CI job managed in Zuul --- .../workflows/functional-blockstorage.yaml | 68 +++++++++++++++++++ .../blockstorage/extensions/backups_test.go | 2 - .../extensions/schedulerhints_test.go | 2 - .../extensions/schedulerstats_test.go | 4 -- .../blockstorage/extensions/services_test.go | 1 - .../extensions/volumeactions_test.go | 2 - .../extensions/volumetenants_test.go | 2 - .../openstack/blockstorage/v3/qos_test.go | 2 - .../blockstorage/v3/quotaset_test.go | 9 --- .../blockstorage/v3/snapshots_test.go | 1 - .../blockstorage/v3/volumeattachments_test.go | 6 -- .../openstack/blockstorage/v3/volumes_test.go | 6 -- .../blockstorage/v3/volumetypes_test.go | 2 - 13 files changed, 68 insertions(+), 39 deletions(-) create mode 100644 .github/workflows/functional-blockstorage.yaml diff --git a/.github/workflows/functional-blockstorage.yaml b/.github/workflows/functional-blockstorage.yaml new file mode 100644 index 0000000000..1ef881f7e3 --- /dev/null +++ b/.github/workflows/functional-blockstorage.yaml @@ -0,0 +1,68 @@ +name: functional-blockstorage +on: + pull_request: + paths: + - '^.*blockstorage.*$' + - '.github/workflows/functional-blockstorage.yaml' + schedule: + - cron: '0 0 * * *' +jobs: + functional-blockstorage: + strategy: + fail-fast: false + matrix: + name: ["master"] + openstack_version: ["master"] + ubuntu_version: ["20.04"] + tests: ["^.*blockstorage/v3.*$"] + include: + - name: "xena" + openstack_version: "stable/xena" + ubuntu_version: "20.04" + tests: "^.*blockstorage/v3.*$" + - name: "wallaby" + openstack_version: "stable/wallaby" + ubuntu_version: "20.04" + tests: "^.*blockstorage/v(2|3).*$" + - name: "victoria" + openstack_version: "stable/victoria" + ubuntu_version: "20.04" + tests: "^.*blockstorage/v(2|3).*$" + - name: "ussuri" + openstack_version: "stable/ussuri" + ubuntu_version: "18.04" + tests: "^.*blockstorage/v(2|3).*$" + - name: "train" + openstack_version: "stable/train" + ubuntu_version: "18.04" + tests: "^.*blockstorage/v(2|3).*$" + runs-on: ubuntu-${{ matrix.ubuntu_version }} + name: Deploy OpenStack ${{ matrix.name }} with Cinder and run blockstorage acceptance tests + steps: + - name: Checkout Gophercloud + uses: actions/checkout@v2 + - name: Deploy devstack + uses: EmilienM/devstack-action@v0.6 + with: + branch: ${{ matrix.openstack_version }} + conf_overrides: | + CINDER_ISCSI_HELPER=tgtadm + enabled_services: 's-account,s-container,s-object,s-proxy,c-bak' + - name: Checkout go + uses: actions/setup-go@v2 + with: + go-version: '^1.15' + - name: Run Gophercloud acceptance tests + run: ./script/acceptancetest + env: + DEVSTACK_PATH: ${{ github.workspace }}/devstack + ACCEPTANCE_TESTS_FILTER: ${{ matrix.tests }} + - name: Generate logs on failure + run: ./script/collectlogs + if: failure() + - name: Upload logs artifacts on failure + if: failure() + uses: actions/upload-artifact@v2 + with: + name: functional-blockstorage-${{ matrix.name }} + path: /tmp/devstack-logs/* diff --git a/acceptance/openstack/blockstorage/extensions/backups_test.go b/acceptance/openstack/blockstorage/extensions/backups_test.go index 4b3a4ff60e..67f86c8eb6 100644 --- a/acceptance/openstack/blockstorage/extensions/backups_test.go +++ b/acceptance/openstack/blockstorage/extensions/backups_test.go @@ -14,8 +14,6 @@ import ( ) func TestBackupsCRUD(t *testing.T) { - clients.SkipRelease(t, "stable/mitaka") - blockClient, err := clients.NewBlockStorageV3Client() th.AssertNoErr(t, err) diff --git a/acceptance/openstack/blockstorage/extensions/schedulerhints_test.go b/acceptance/openstack/blockstorage/extensions/schedulerhints_test.go index f86713d384..8ff9fbb6a1 100644 --- a/acceptance/openstack/blockstorage/extensions/schedulerhints_test.go +++ b/acceptance/openstack/blockstorage/extensions/schedulerhints_test.go @@ -14,8 +14,6 @@ import ( ) func TestSchedulerHints(t *testing.T) { - clients.SkipRelease(t, "stable/mitaka") - clients.RequireLong(t) client, err := clients.NewBlockStorageV3Client() diff --git a/acceptance/openstack/blockstorage/extensions/schedulerstats_test.go b/acceptance/openstack/blockstorage/extensions/schedulerstats_test.go index 7e4f01ccde..7b5f609b1c 100644 --- a/acceptance/openstack/blockstorage/extensions/schedulerstats_test.go +++ b/acceptance/openstack/blockstorage/extensions/schedulerstats_test.go @@ -14,10 +14,6 @@ import ( func TestSchedulerStatsList(t *testing.T) { clients.RequireAdmin(t) - clients.SkipRelease(t, "stable/mitaka") - clients.SkipRelease(t, "stable/newton") - clients.SkipRelease(t, "stable/ocata") - clients.SkipRelease(t, "stable/pike") blockClient, err := clients.NewBlockStorageV3Client() th.AssertNoErr(t, err) diff --git a/acceptance/openstack/blockstorage/extensions/services_test.go b/acceptance/openstack/blockstorage/extensions/services_test.go index 301155bcfd..863d38aaec 100644 --- a/acceptance/openstack/blockstorage/extensions/services_test.go +++ b/acceptance/openstack/blockstorage/extensions/services_test.go @@ -13,7 +13,6 @@ import ( ) func TestServicesList(t *testing.T) { - clients.SkipRelease(t, "stable/mitaka") clients.RequireAdmin(t) blockClient, err := clients.NewBlockStorageV3Client() diff --git a/acceptance/openstack/blockstorage/extensions/volumeactions_test.go b/acceptance/openstack/blockstorage/extensions/volumeactions_test.go index f0f1365df0..264d0efc94 100644 --- a/acceptance/openstack/blockstorage/extensions/volumeactions_test.go +++ b/acceptance/openstack/blockstorage/extensions/volumeactions_test.go @@ -16,8 +16,6 @@ import ( ) func TestVolumeActionsUploadImageDestroy(t *testing.T) { - t.Skip("Currently failing in OpenLab") - blockClient, err := clients.NewBlockStorageV3Client() th.AssertNoErr(t, err) diff --git a/acceptance/openstack/blockstorage/extensions/volumetenants_test.go b/acceptance/openstack/blockstorage/extensions/volumetenants_test.go index 8b4e2cbf71..e684a79423 100644 --- a/acceptance/openstack/blockstorage/extensions/volumetenants_test.go +++ b/acceptance/openstack/blockstorage/extensions/volumetenants_test.go @@ -14,8 +14,6 @@ import ( ) func TestVolumeTenants(t *testing.T) { - clients.SkipRelease(t, "stable/mitaka") - type volumeWithTenant struct { volumes.Volume volumetenants.VolumeTenantExt diff --git a/acceptance/openstack/blockstorage/v3/qos_test.go b/acceptance/openstack/blockstorage/v3/qos_test.go index 831ae53a1c..82c52a384a 100644 --- a/acceptance/openstack/blockstorage/v3/qos_test.go +++ b/acceptance/openstack/blockstorage/v3/qos_test.go @@ -11,7 +11,6 @@ import ( ) func TestQoS(t *testing.T) { - clients.SkipRelease(t, "stable/mitaka") clients.RequireAdmin(t) client, err := clients.NewBlockStorageV3Client() @@ -76,7 +75,6 @@ func TestQoS(t *testing.T) { } func TestQoSAssociations(t *testing.T) { - clients.SkipRelease(t, "stable/mitaka") clients.RequireAdmin(t) client, err := clients.NewBlockStorageV3Client() diff --git a/acceptance/openstack/blockstorage/v3/quotaset_test.go b/acceptance/openstack/blockstorage/v3/quotaset_test.go index 70b365262f..bffe0793d1 100644 --- a/acceptance/openstack/blockstorage/v3/quotaset_test.go +++ b/acceptance/openstack/blockstorage/v3/quotaset_test.go @@ -16,7 +16,6 @@ import ( ) func TestQuotasetGet(t *testing.T) { - clients.SkipRelease(t, "stable/mitaka") clients.RequireAdmin(t) client, projectID := getClientAndProject(t) @@ -28,7 +27,6 @@ func TestQuotasetGet(t *testing.T) { } func TestQuotasetGetDefaults(t *testing.T) { - clients.SkipRelease(t, "stable/mitaka") clients.RequireAdmin(t) client, projectID := getClientAndProject(t) @@ -40,7 +38,6 @@ func TestQuotasetGetDefaults(t *testing.T) { } func TestQuotasetGetUsage(t *testing.T) { - clients.SkipRelease(t, "stable/mitaka") clients.RequireAdmin(t) client, projectID := getClientAndProject(t) @@ -83,9 +80,6 @@ var VolumeTypeCreateOpts = volumetypes.CreateOpts{ } func TestQuotasetUpdate(t *testing.T) { - clients.SkipRelease(t, "stable/mitaka") - clients.SkipRelease(t, "stable/newton") - clients.SkipRelease(t, "stable/ocata") clients.RequireAdmin(t) client, projectID := getClientAndProject(t) @@ -144,9 +138,6 @@ func TestQuotasetUpdate(t *testing.T) { } func TestQuotasetDelete(t *testing.T) { - clients.SkipRelease(t, "stable/mitaka") - clients.SkipRelease(t, "stable/newton") - clients.SkipRelease(t, "stable/ocata") clients.RequireAdmin(t) client, projectID := getClientAndProject(t) diff --git a/acceptance/openstack/blockstorage/v3/snapshots_test.go b/acceptance/openstack/blockstorage/v3/snapshots_test.go index 748f9ee4ef..06ad8e8ba0 100644 --- a/acceptance/openstack/blockstorage/v3/snapshots_test.go +++ b/acceptance/openstack/blockstorage/v3/snapshots_test.go @@ -14,7 +14,6 @@ import ( ) func TestSnapshots(t *testing.T) { - clients.SkipRelease(t, "stable/mitaka") clients.RequireLong(t) client, err := clients.NewBlockStorageV3Client() diff --git a/acceptance/openstack/blockstorage/v3/volumeattachments_test.go b/acceptance/openstack/blockstorage/v3/volumeattachments_test.go index 4b6963998d..a8cd3b005e 100644 --- a/acceptance/openstack/blockstorage/v3/volumeattachments_test.go +++ b/acceptance/openstack/blockstorage/v3/volumeattachments_test.go @@ -13,12 +13,6 @@ import ( ) func TestVolumeAttachments(t *testing.T) { - t.Skip("Currently failing in OpenLab") - - clients.SkipRelease(t, "stable/mitaka") - clients.SkipRelease(t, "stable/newton") - clients.SkipRelease(t, "stable/ocata") - blockClient, err := clients.NewBlockStorageV3Client() th.AssertNoErr(t, err) diff --git a/acceptance/openstack/blockstorage/v3/volumes_test.go b/acceptance/openstack/blockstorage/v3/volumes_test.go index f47545b702..186b32f039 100644 --- a/acceptance/openstack/blockstorage/v3/volumes_test.go +++ b/acceptance/openstack/blockstorage/v3/volumes_test.go @@ -15,7 +15,6 @@ import ( ) func TestVolumes(t *testing.T) { - clients.SkipRelease(t, "stable/mitaka") clients.RequireLong(t) client, err := clients.NewBlockStorageV3Client() @@ -69,10 +68,6 @@ func TestVolumes(t *testing.T) { func TestVolumesMultiAttach(t *testing.T) { clients.RequireLong(t) - clients.SkipRelease(t, "stable/mitaka") - clients.SkipRelease(t, "stable/newton") - clients.SkipRelease(t, "stable/ocata") - clients.SkipRelease(t, "stable/pike") client, err := clients.NewBlockStorageV3Client() th.AssertNoErr(t, err) @@ -99,7 +94,6 @@ func TestVolumesMultiAttach(t *testing.T) { } func TestVolumesCascadeDelete(t *testing.T) { - clients.SkipRelease(t, "stable/mitaka") clients.RequireLong(t) client, err := clients.NewBlockStorageV3Client() diff --git a/acceptance/openstack/blockstorage/v3/volumetypes_test.go b/acceptance/openstack/blockstorage/v3/volumetypes_test.go index 2029deb0ba..b640a63f55 100644 --- a/acceptance/openstack/blockstorage/v3/volumetypes_test.go +++ b/acceptance/openstack/blockstorage/v3/volumetypes_test.go @@ -14,7 +14,6 @@ import ( ) func TestVolumeTypes(t *testing.T) { - clients.SkipRelease(t, "stable/mitaka") clients.RequireAdmin(t) client, err := clients.NewBlockStorageV3Client() @@ -59,7 +58,6 @@ func TestVolumeTypes(t *testing.T) { } func TestVolumeTypesExtraSpecs(t *testing.T) { - clients.SkipRelease(t, "stable/mitaka") clients.RequireAdmin(t) client, err := clients.NewBlockStorageV3Client() From 300b0133b4129496dec751491538bc0bcd5e09b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= <m.andre@redhat.com> Date: Fri, 19 Nov 2021 16:52:53 +0100 Subject: [PATCH 030/360] Add SkipReleasesAbove() helper function for acceptance tests The newly added SkipReleasesAbove() function skip tests when running above a certain release version. Useful for when features are deprecated in OpenStack. (cherry picked from commit 9f8d3ee1edf223b0ca00e1dfe0276c749d0b4570) --- acceptance/clients/conditions.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/acceptance/clients/conditions.go b/acceptance/clients/conditions.go index 309cdb8cf7..4a46d07b6f 100644 --- a/acceptance/clients/conditions.go +++ b/acceptance/clients/conditions.go @@ -99,3 +99,15 @@ func SkipReleasesBelow(t *testing.T, release string) { t.Skipf("this is not supported below %s, testing in %s", release, current_branch) } } + +// SkipReleasesAbove will have the test be skipped on releases above a certain +// one. The test is always skipped on master release. Releases are named such +// as 'stable/mitaka', master, etc. +func SkipReleasesAbove(t *testing.T, release string) { + current_branch := os.Getenv("OS_BRANCH") + + // Assume master is always too new + if current_branch == "master" || current_branch > release { + t.Skipf("this is not supported above %s, testing in %s", release, current_branch) + } +} From 807dcac4fbcea4695b0d99ec43c7bc83539aa817 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= <m.andre@redhat.com> Date: Fri, 19 Nov 2021 17:56:59 +0100 Subject: [PATCH 031/360] Skip blockstorage v1 acceptance tests above icehouse Cinder v1 API was deprecated in Juno [1] and therefore the acceptance test should be skipped above Icehouse. [1] https://docs.openstack.org/releasenotes/cinder/queens.html#queens-series-release-notes-12-0-0-stable-queens-upgrade-notes (cherry picked from commit 1f580646398075ac79d13bed081564f8effb21de) --- acceptance/openstack/blockstorage/v1/snapshots_test.go | 2 ++ acceptance/openstack/blockstorage/v1/volumes_test.go | 2 ++ acceptance/openstack/blockstorage/v1/volumetypes_test.go | 2 ++ 3 files changed, 6 insertions(+) diff --git a/acceptance/openstack/blockstorage/v1/snapshots_test.go b/acceptance/openstack/blockstorage/v1/snapshots_test.go index ff0a2d07e3..9ff2c192a4 100644 --- a/acceptance/openstack/blockstorage/v1/snapshots_test.go +++ b/acceptance/openstack/blockstorage/v1/snapshots_test.go @@ -12,6 +12,7 @@ import ( ) func TestSnapshotsList(t *testing.T) { + clients.SkipReleasesAbove(t, "stable/icehouse") client, err := clients.NewBlockStorageV1Client() if err != nil { t.Fatalf("Unable to create a blockstorage client: %v", err) @@ -33,6 +34,7 @@ func TestSnapshotsList(t *testing.T) { } func TestSnapshotsCreateDelete(t *testing.T) { + clients.SkipReleasesAbove(t, "stable/icehouse") client, err := clients.NewBlockStorageV1Client() if err != nil { t.Fatalf("Unable to create a blockstorage client: %v", err) diff --git a/acceptance/openstack/blockstorage/v1/volumes_test.go b/acceptance/openstack/blockstorage/v1/volumes_test.go index 98204724bb..c5aba5d539 100644 --- a/acceptance/openstack/blockstorage/v1/volumes_test.go +++ b/acceptance/openstack/blockstorage/v1/volumes_test.go @@ -13,6 +13,7 @@ import ( ) func TestVolumesList(t *testing.T) { + clients.SkipReleasesAbove(t, "stable/icehouse") client, err := clients.NewBlockStorageV1Client() if err != nil { t.Fatalf("Unable to create a blockstorage client: %v", err) @@ -34,6 +35,7 @@ func TestVolumesList(t *testing.T) { } func TestVolumesCreateDestroy(t *testing.T) { + clients.SkipReleasesAbove(t, "stable/icehouse") client, err := clients.NewBlockStorageV1Client() if err != nil { t.Fatalf("Unable to create blockstorage client: %v", err) diff --git a/acceptance/openstack/blockstorage/v1/volumetypes_test.go b/acceptance/openstack/blockstorage/v1/volumetypes_test.go index 390df09587..c1e76a740e 100644 --- a/acceptance/openstack/blockstorage/v1/volumetypes_test.go +++ b/acceptance/openstack/blockstorage/v1/volumetypes_test.go @@ -12,6 +12,7 @@ import ( ) func TestVolumeTypesList(t *testing.T) { + clients.SkipReleasesAbove(t, "stable/icehouse") client, err := clients.NewBlockStorageV1Client() if err != nil { t.Fatalf("Unable to create a blockstorage client: %v", err) @@ -33,6 +34,7 @@ func TestVolumeTypesList(t *testing.T) { } func TestVolumeTypesCreateDestroy(t *testing.T) { + clients.SkipReleasesAbove(t, "stable/icehouse") client, err := clients.NewBlockStorageV1Client() if err != nil { t.Fatalf("Unable to create a blockstorage client: %v", err) From a7b2ea9d51c67817f12141579d05f1aad578f42a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= <m.andre@redhat.com> Date: Fri, 19 Nov 2021 17:59:32 +0100 Subject: [PATCH 032/360] Skip blockstorage v2 acceptance tests above ocata Cinder v2 API was deprecated in Pike [1] and therefore the acceptance test should be skipped above Ocata. [1] https://docs.openstack.org/releasenotes/cinder/pike.html#deprecation-notes (cherry picked from commit a76a55d8a300e5aa86248bcf6c10b309728af2ba) --- acceptance/openstack/blockstorage/v2/snapshots_test.go | 1 + acceptance/openstack/blockstorage/v2/volumes_test.go | 3 +++ 2 files changed, 4 insertions(+) diff --git a/acceptance/openstack/blockstorage/v2/snapshots_test.go b/acceptance/openstack/blockstorage/v2/snapshots_test.go index 0c5df1d446..22a8a6bd8a 100644 --- a/acceptance/openstack/blockstorage/v2/snapshots_test.go +++ b/acceptance/openstack/blockstorage/v2/snapshots_test.go @@ -13,6 +13,7 @@ import ( ) func TestSnapshots(t *testing.T) { + clients.SkipReleasesAbove(t, "stable/ocata") clients.RequireLong(t) client, err := clients.NewBlockStorageV2Client() diff --git a/acceptance/openstack/blockstorage/v2/volumes_test.go b/acceptance/openstack/blockstorage/v2/volumes_test.go index 569918e5ca..f2f839cc98 100644 --- a/acceptance/openstack/blockstorage/v2/volumes_test.go +++ b/acceptance/openstack/blockstorage/v2/volumes_test.go @@ -15,6 +15,7 @@ import ( ) func TestVolumesCreateDestroy(t *testing.T) { + clients.SkipReleasesAbove(t, "stable/ocata") clients.RequireLong(t) client, err := clients.NewBlockStorageV2Client() @@ -59,6 +60,7 @@ func TestVolumesCreateDestroy(t *testing.T) { } func TestVolumesCreateForceDestroy(t *testing.T) { + clients.SkipReleasesAbove(t, "stable/ocata") clients.RequireLong(t) client, err := clients.NewBlockStorageV2Client() @@ -77,6 +79,7 @@ func TestVolumesCreateForceDestroy(t *testing.T) { } func TestVolumesCascadeDelete(t *testing.T) { + clients.SkipReleasesAbove(t, "stable/ocata") clients.RequireLong(t) client, err := clients.NewBlockStorageV2Client() From 62b8b9f1b72414119d90ab23c4451120fd4a1b6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= <m.andre@redhat.com> Date: Fri, 19 Nov 2021 18:07:26 +0100 Subject: [PATCH 033/360] Use blockstorage v3 for noauth acceptance tests Blockstorage v2 was deprecated in Pike, we should now use v3. (cherry picked from commit 9bae6260820779df89a7c031646956a041eceb5a) --- acceptance/openstack/blockstorage/noauth/blockstorage.go | 4 ++-- acceptance/openstack/blockstorage/noauth/snapshots_test.go | 6 +++--- acceptance/openstack/blockstorage/noauth/volumes_test.go | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/acceptance/openstack/blockstorage/noauth/blockstorage.go b/acceptance/openstack/blockstorage/noauth/blockstorage.go index ca133d8d5c..d057c2e604 100644 --- a/acceptance/openstack/blockstorage/noauth/blockstorage.go +++ b/acceptance/openstack/blockstorage/noauth/blockstorage.go @@ -9,8 +9,8 @@ import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" - "github.com/gophercloud/gophercloud/openstack/blockstorage/v2/snapshots" - "github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes" + "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/snapshots" + "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes" ) // CreateVolume will create a volume with a random name and size of 1GB. An diff --git a/acceptance/openstack/blockstorage/noauth/snapshots_test.go b/acceptance/openstack/blockstorage/noauth/snapshots_test.go index 6d16e1a23c..2c0cc63dec 100644 --- a/acceptance/openstack/blockstorage/noauth/snapshots_test.go +++ b/acceptance/openstack/blockstorage/noauth/snapshots_test.go @@ -8,11 +8,11 @@ import ( "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" - "github.com/gophercloud/gophercloud/openstack/blockstorage/v2/snapshots" + "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/snapshots" ) func TestSnapshotsList(t *testing.T) { - client, err := clients.NewBlockStorageV2NoAuthClient() + client, err := clients.NewBlockStorageV3NoAuthClient() if err != nil { t.Fatalf("Unable to create a blockstorage client: %v", err) } @@ -33,7 +33,7 @@ func TestSnapshotsList(t *testing.T) { } func TestSnapshotsCreateDelete(t *testing.T) { - client, err := clients.NewBlockStorageV2NoAuthClient() + client, err := clients.NewBlockStorageV3NoAuthClient() if err != nil { t.Fatalf("Unable to create a blockstorage client: %v", err) } diff --git a/acceptance/openstack/blockstorage/noauth/volumes_test.go b/acceptance/openstack/blockstorage/noauth/volumes_test.go index 47891719d4..5f80d07cfb 100644 --- a/acceptance/openstack/blockstorage/noauth/volumes_test.go +++ b/acceptance/openstack/blockstorage/noauth/volumes_test.go @@ -8,11 +8,11 @@ import ( "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" - "github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes" + "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes" ) func TestVolumesList(t *testing.T) { - client, err := clients.NewBlockStorageV2NoAuthClient() + client, err := clients.NewBlockStorageV3NoAuthClient() if err != nil { t.Fatalf("Unable to create a blockstorage client: %v", err) } @@ -33,7 +33,7 @@ func TestVolumesList(t *testing.T) { } func TestVolumesCreateDestroy(t *testing.T) { - client, err := clients.NewBlockStorageV2NoAuthClient() + client, err := clients.NewBlockStorageV3NoAuthClient() if err != nil { t.Fatalf("Unable to create blockstorage client: %v", err) } From 5f686ba7165dbe066cfe4ced2f6b3e07efd4d4b2 Mon Sep 17 00:00:00 2001 From: Emilien Macchi <emilien@redhat.com> Date: Fri, 14 Jan 2022 09:44:10 -0500 Subject: [PATCH 034/360] CI-v2: simplify blockstorage acceptance regex --- .github/workflows/functional-blockstorage.yaml | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/.github/workflows/functional-blockstorage.yaml b/.github/workflows/functional-blockstorage.yaml index 1ef881f7e3..208808155d 100644 --- a/.github/workflows/functional-blockstorage.yaml +++ b/.github/workflows/functional-blockstorage.yaml @@ -14,28 +14,22 @@ jobs: name: ["master"] openstack_version: ["master"] ubuntu_version: ["20.04"] - tests: ["^.*blockstorage/v3.*$"] include: - name: "xena" openstack_version: "stable/xena" ubuntu_version: "20.04" - tests: "^.*blockstorage/v3.*$" - name: "wallaby" openstack_version: "stable/wallaby" ubuntu_version: "20.04" - tests: "^.*blockstorage/v(2|3).*$" - name: "victoria" openstack_version: "stable/victoria" ubuntu_version: "20.04" - tests: "^.*blockstorage/v(2|3).*$" - name: "ussuri" openstack_version: "stable/ussuri" ubuntu_version: "18.04" - tests: "^.*blockstorage/v(2|3).*$" - name: "train" openstack_version: "stable/train" ubuntu_version: "18.04" - tests: "^.*blockstorage/v(2|3).*$" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Deploy OpenStack ${{ matrix.name }} with Cinder and run blockstorage acceptance tests steps: @@ -56,7 +50,8 @@ jobs: run: ./script/acceptancetest env: DEVSTACK_PATH: ${{ github.workspace }}/devstack - ACCEPTANCE_TESTS_FILTER: ${{ matrix.tests }} + ACCEPTANCE_TESTS_FILTER: ^.*blockstorage(.(?!noauth).*)?$ + OS_BRANCH: ${{ matrix.openstack_version }} - name: Generate logs on failure run: ./script/collectlogs if: failure() From 1578c689411031da741f8bdf7faa15940460869b Mon Sep 17 00:00:00 2001 From: Emilien Macchi <emilien@redhat.com> Date: Mon, 17 Jan 2022 16:24:25 -0500 Subject: [PATCH 035/360] [CI-v2] Add imageservice job with Github Action This runs a Github Action to: * Deploy Devstack with Glance * Run acceptance/openstack/imageservice * Unskip all the tests * Post logs if any failure --- .../workflows/functional-imageservice.yaml | 59 +++++++++++++++++++ .../imageservice/v2/imagedata_test.go | 7 --- .../imageservice/v2/imageimport_test.go | 12 ---- 3 files changed, 59 insertions(+), 19 deletions(-) create mode 100644 .github/workflows/functional-imageservice.yaml diff --git a/.github/workflows/functional-imageservice.yaml b/.github/workflows/functional-imageservice.yaml new file mode 100644 index 0000000000..1bcb8bf014 --- /dev/null +++ b/.github/workflows/functional-imageservice.yaml @@ -0,0 +1,59 @@ +name: functional-imageservice +on: + pull_request: + paths: + - '^.*imageservice.*$' + - '.github/workflows/functional-imageservice.yaml' + schedule: + - cron: '0 0 * * *' +jobs: + functional-imageservice: + strategy: + fail-fast: false + matrix: + name: ["master"] + openstack_version: ["master"] + ubuntu_version: ["20.04"] + include: + - name: "xena" + openstack_version: "stable/xena" + ubuntu_version: "20.04" + - name: "wallaby" + openstack_version: "stable/wallaby" + ubuntu_version: "20.04" + - name: "victoria" + openstack_version: "stable/victoria" + ubuntu_version: "20.04" + - name: "ussuri" + openstack_version: "stable/ussuri" + ubuntu_version: "18.04" + - name: "train" + openstack_version: "stable/train" + ubuntu_version: "18.04" + runs-on: ubuntu-${{ matrix.ubuntu_version }} + name: Deploy OpenStack ${{ matrix.name }} with Glance and run imageservice acceptance tests + steps: + - name: Checkout Gophercloud + uses: actions/checkout@v2 + - name: Deploy devstack + uses: EmilienM/devstack-action@v0.6 + with: + branch: ${{ matrix.openstack_version }} + - name: Checkout go + uses: actions/setup-go@v2 + with: + go-version: '^1.15' + - name: Run Gophercloud acceptance tests + run: ./script/acceptancetest + env: + DEVSTACK_PATH: ${{ github.workspace }}/devstack + ACCEPTANCE_TESTS_FILTER: "^.*imageservice.*$" + - name: Generate logs on failure + run: ./script/collectlogs + if: failure() + - name: Upload logs artifacts on failure + if: failure() + uses: actions/upload-artifact@v2 + with: + name: functional-imageservice-${{ matrix.name }} + path: /tmp/devstack-logs/* diff --git a/acceptance/openstack/imageservice/v2/imagedata_test.go b/acceptance/openstack/imageservice/v2/imagedata_test.go index 2ba59b2cdc..d19c38d4a9 100644 --- a/acceptance/openstack/imageservice/v2/imagedata_test.go +++ b/acceptance/openstack/imageservice/v2/imagedata_test.go @@ -9,13 +9,6 @@ import ( ) func TestImageStage(t *testing.T) { - clients.SkipRelease(t, "stable/mitaka") - clients.SkipRelease(t, "stable/newton") - clients.SkipRelease(t, "stable/ocata") - clients.SkipRelease(t, "stable/pike") - clients.SkipRelease(t, "stable/queens") - clients.SkipRelease(t, "stable/rocky") - client, err := clients.NewImageServiceV2Client() th.AssertNoErr(t, err) diff --git a/acceptance/openstack/imageservice/v2/imageimport_test.go b/acceptance/openstack/imageservice/v2/imageimport_test.go index 9a9bd4f23e..53dbea4d3d 100644 --- a/acceptance/openstack/imageservice/v2/imageimport_test.go +++ b/acceptance/openstack/imageservice/v2/imageimport_test.go @@ -12,12 +12,6 @@ import ( ) func TestGetImportInfo(t *testing.T) { - clients.SkipRelease(t, "stable/mitaka") - clients.SkipRelease(t, "stable/newton") - clients.SkipRelease(t, "stable/ocata") - clients.SkipRelease(t, "stable/pike") - clients.SkipRelease(t, "stable/queens") - client, err := clients.NewImageServiceV2Client() th.AssertNoErr(t, err) @@ -28,12 +22,6 @@ func TestGetImportInfo(t *testing.T) { } func TestCreateImport(t *testing.T) { - clients.SkipRelease(t, "stable/mitaka") - clients.SkipRelease(t, "stable/newton") - clients.SkipRelease(t, "stable/ocata") - clients.SkipRelease(t, "stable/pike") - clients.SkipRelease(t, "stable/queens") - client, err := clients.NewImageServiceV2Client() th.AssertNoErr(t, err) From 27b4c751c4270c361ba94fea01526efb6315e4eb Mon Sep 17 00:00:00 2001 From: nikParasyr <nik.parasyr@protonmail.com> Date: Tue, 18 Jan 2022 14:48:47 +0100 Subject: [PATCH 036/360] Update loadbalancer/v2/l7policies create and update Add REDIRECT_PREFIX and REDIRECT_HTTP_CODE to l7policy CreateOpts and UpdateOpts. Docs: https://docs.openstack.org/api-ref/load-balancer/v2/?expanded=create-an-l7-policy-detail,update-a-l7-policy-detail#create-an-l7-policy For: #2322 --- .../loadbalancer/v2/l7policies/requests.go | 31 +++++++++++++++++-- .../loadbalancer/v2/l7policies/results.go | 10 +++++- 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/openstack/loadbalancer/v2/l7policies/requests.go b/openstack/loadbalancer/v2/l7policies/requests.go index 1969ba0367..2b84bcad14 100644 --- a/openstack/loadbalancer/v2/l7policies/requests.go +++ b/openstack/loadbalancer/v2/l7policies/requests.go @@ -16,6 +16,7 @@ type RuleType string type CompareType string const ( + ActionRedirectPrefix Action = "REDIRECT_PREFIX" ActionRedirectToPool Action = "REDIRECT_TO_POOL" ActionRedirectToURL Action = "REDIRECT_TO_URL" ActionReject Action = "REJECT" @@ -42,7 +43,7 @@ type CreateOpts struct { // The ID of the listener. ListenerID string `json:"listener_id,omitempty"` - // The L7 policy action. One of REDIRECT_TO_POOL, REDIRECT_TO_URL, or REJECT. + // The L7 policy action. One of REDIRECT_PREFIX, REDIRECT_TO_POOL, REDIRECT_TO_URL, or REJECT. Action Action `json:"action" required:"true"` // The position of this policy on the listener. @@ -55,6 +56,10 @@ type CreateOpts struct { // Only administrative users can specify a project UUID other than their own. ProjectID string `json:"project_id,omitempty"` + // Requests matching this policy will be redirected to this Prefix URL. + // Only valid if action is REDIRECT_PREFIX. + RedirectPrefix string `json:"redirect_prefix,omitempty"` + // Requests matching this policy will be redirected to the pool with this ID. // Only valid if action is REDIRECT_TO_POOL. RedirectPoolID string `json:"redirect_pool_id,omitempty"` @@ -63,6 +68,11 @@ type CreateOpts struct { // Only valid if action is REDIRECT_TO_URL. RedirectURL string `json:"redirect_url,omitempty"` + // Requests matching this policy will be redirected to the specified URL or Prefix URL + // with the HTTP response code. Valid if action is REDIRECT_TO_URL or REDIRECT_PREFIX. + // Valid options are: 301, 302, 303, 307, or 308. Default is 302. Requires version 2.9 + RedirectHttpCode int32 `json:"redirect_http_code,omitempty"` + // The administrative state of the Loadbalancer. A valid value is true (UP) // or false (DOWN). AdminStateUp *bool `json:"admin_state_up,omitempty"` @@ -169,7 +179,7 @@ type UpdateOpts struct { // Name of the L7 policy, empty string is allowed. Name *string `json:"name,omitempty"` - // The L7 policy action. One of REDIRECT_TO_POOL, REDIRECT_TO_URL, or REJECT. + // The L7 policy action. One of REDIRECT_PREFIX, REDIRECT_TO_POOL, REDIRECT_TO_URL, or REJECT. Action Action `json:"action,omitempty"` // The position of this policy on the listener. @@ -178,6 +188,10 @@ type UpdateOpts struct { // A human-readable description for the resource, empty string is allowed. Description *string `json:"description,omitempty"` + // Requests matching this policy will be redirected to this Prefix URL. + // Only valid if action is REDIRECT_PREFIX. + RedirectPrefix *string `json:"redirect_prefix,omitempty"` + // Requests matching this policy will be redirected to the pool with this ID. // Only valid if action is REDIRECT_TO_POOL. RedirectPoolID *string `json:"redirect_pool_id,omitempty"` @@ -186,6 +200,11 @@ type UpdateOpts struct { // Only valid if action is REDIRECT_TO_URL. RedirectURL *string `json:"redirect_url,omitempty"` + // Requests matching this policy will be redirected to the specified URL or Prefix URL + // with the HTTP response code. Valid if action is REDIRECT_TO_URL or REDIRECT_PREFIX. + // Valid options are: 301, 302, 303, 307, or 308. Default is 302. Requires version 2.9 + RedirectHttpCode int32 `json:"redirect_http_code,omitempty"` + // The administrative state of the Loadbalancer. A valid value is true (UP) // or false (DOWN). AdminStateUp *bool `json:"admin_state_up,omitempty"` @@ -208,6 +227,14 @@ func (opts UpdateOpts) ToL7PolicyUpdateMap() (map[string]interface{}, error) { m["redirect_url"] = nil } + if m["redirect_prefix"] == "" { + m["redirect_prefix"] = nil + } + + if m["redirect_http_code"] == 0 { + m["redirect_http_code"] = nil + } + return b, nil } diff --git a/openstack/loadbalancer/v2/l7policies/results.go b/openstack/loadbalancer/v2/l7policies/results.go index dafcceed14..cf236d742c 100644 --- a/openstack/loadbalancer/v2/l7policies/results.go +++ b/openstack/loadbalancer/v2/l7policies/results.go @@ -17,7 +17,7 @@ type L7Policy struct { // The ID of the listener. ListenerID string `json:"listener_id"` - // The L7 policy action. One of REDIRECT_TO_POOL, REDIRECT_TO_URL, or REJECT. + // The L7 policy action. One of REDIRECT_PREFIX, REDIRECT_TO_POOL, REDIRECT_TO_URL, or REJECT. Action string `json:"action"` // The position of this policy on the listener. @@ -34,10 +34,18 @@ type L7Policy struct { // Only valid if action is REDIRECT_TO_POOL. RedirectPoolID string `json:"redirect_pool_id"` + // Requests matching this policy will be redirected to this Prefix URL. + // Only valid if action is REDIRECT_PREFIX. + RedirectPrefix string `json:"redirect_prefix"` + // Requests matching this policy will be redirected to this URL. // Only valid if action is REDIRECT_TO_URL. RedirectURL string `json:"redirect_url"` + // Requests matching this policy will be redirected to the specified URL or Prefix URL + // with the HTTP response code. Valid if action is REDIRECT_TO_URL or REDIRECT_PREFIX. + RedirectHttpCode int32 `json:"redirect_http_code"` + // The administrative state of the L7 policy, which is up (true) or down (false). AdminStateUp bool `json:"admin_state_up"` From d8138dc34add061e5c5a213a856cfa3fc03a2a34 Mon Sep 17 00:00:00 2001 From: Emilien Macchi <emilien@redhat.com> Date: Fri, 21 Jan 2022 10:11:50 -0500 Subject: [PATCH 037/360] [CI-v2] Add keymanager job with Github Action * Deploy Barbican with Devstack * Run keymanager acceptance tests * Remove all the skips * Logs failures if any --- .github/workflows/functional-keymanager.yaml | 62 +++++++++++++++++++ .../openstack/keymanager/v1/acls_test.go | 4 -- .../keymanager/v1/containers_test.go | 16 ----- .../openstack/keymanager/v1/orders_test.go | 6 -- .../openstack/keymanager/v1/secrets_test.go | 32 ---------- 5 files changed, 62 insertions(+), 58 deletions(-) create mode 100644 .github/workflows/functional-keymanager.yaml diff --git a/.github/workflows/functional-keymanager.yaml b/.github/workflows/functional-keymanager.yaml new file mode 100644 index 0000000000..5ea15a1920 --- /dev/null +++ b/.github/workflows/functional-keymanager.yaml @@ -0,0 +1,62 @@ +name: functional-keymanager +on: + pull_request: + paths: + - '^.*keymanager.*$' + - '.github/workflows/functional-keymanager.yaml' + schedule: + - cron: '0 0 * * *' +jobs: + functional-keymanager: + strategy: + fail-fast: false + matrix: + name: ["master"] + openstack_version: ["master"] + ubuntu_version: ["20.04"] + include: + - name: "xena" + openstack_version: "stable/xena" + ubuntu_version: "20.04" + - name: "wallaby" + openstack_version: "stable/wallaby" + ubuntu_version: "20.04" + - name: "victoria" + openstack_version: "stable/victoria" + ubuntu_version: "20.04" + - name: "ussuri" + openstack_version: "stable/ussuri" + ubuntu_version: "18.04" + - name: "train" + openstack_version: "stable/train" + ubuntu_version: "18.04" + runs-on: ubuntu-${{ matrix.ubuntu_version }} + name: Deploy OpenStack ${{ matrix.name }} with Barbican and run keymanager acceptance tests + steps: + - name: Checkout Gophercloud + uses: actions/checkout@v2 + - name: Deploy devstack + uses: EmilienM/devstack-action@v0.6 + with: + branch: ${{ matrix.openstack_version }} + conf_overrides: | + enable_plugin barbican https://opendev.org/openstack/barbican ${{ matrix.openstack_version }} + enabled_services: 'barbican-svc,barbican-retry,barbican-keystone-listener' + - name: Checkout go + uses: actions/setup-go@v2 + with: + go-version: '^1.15' + - name: Run Gophercloud acceptance tests + run: ./script/acceptancetest + env: + DEVSTACK_PATH: ${{ github.workspace }}/devstack + ACCEPTANCE_TESTS_FILTER: "^.*keymanager.*$" + - name: Generate logs on failure + run: ./script/collectlogs + if: failure() + - name: Upload logs artifacts on failure + if: failure() + uses: actions/upload-artifact@v2 + with: + name: functional-keymanager-${{ matrix.name }} + path: /tmp/devstack-logs/* diff --git a/acceptance/openstack/keymanager/v1/acls_test.go b/acceptance/openstack/keymanager/v1/acls_test.go index 1ea443bb3c..5defa5b78e 100644 --- a/acceptance/openstack/keymanager/v1/acls_test.go +++ b/acceptance/openstack/keymanager/v1/acls_test.go @@ -13,10 +13,6 @@ import ( ) func TestACLCRUD(t *testing.T) { - clients.SkipRelease(t, "stable/mitaka") - clients.SkipRelease(t, "stable/newton") - clients.SkipRelease(t, "stable/queens") - client, err := clients.NewKeyManagerV1Client() th.AssertNoErr(t, err) diff --git a/acceptance/openstack/keymanager/v1/containers_test.go b/acceptance/openstack/keymanager/v1/containers_test.go index f369eef44e..b4699b3dee 100644 --- a/acceptance/openstack/keymanager/v1/containers_test.go +++ b/acceptance/openstack/keymanager/v1/containers_test.go @@ -14,10 +14,6 @@ import ( ) func TestGenericContainersCRUD(t *testing.T) { - clients.SkipRelease(t, "stable/mitaka") - clients.SkipRelease(t, "stable/newton") - clients.SkipRelease(t, "stable/queens") - client, err := clients.NewKeyManagerV1Client() th.AssertNoErr(t, err) @@ -61,10 +57,6 @@ func TestGenericContainersCRUD(t *testing.T) { } func TestCertificateContainer(t *testing.T) { - clients.SkipRelease(t, "stable/mitaka") - clients.SkipRelease(t, "stable/newton") - clients.SkipRelease(t, "stable/queens") - client, err := clients.NewKeyManagerV1Client() th.AssertNoErr(t, err) @@ -109,10 +101,6 @@ func TestCertificateContainer(t *testing.T) { } func TestRSAContainer(t *testing.T) { - clients.SkipRelease(t, "stable/mitaka") - clients.SkipRelease(t, "stable/newton") - clients.SkipRelease(t, "stable/queens") - client, err := clients.NewKeyManagerV1Client() th.AssertNoErr(t, err) @@ -157,10 +145,6 @@ func TestRSAContainer(t *testing.T) { } func TestContainerConsumersCRUD(t *testing.T) { - clients.SkipRelease(t, "stable/mitaka") - clients.SkipRelease(t, "stable/newton") - clients.SkipRelease(t, "stable/queens") - client, err := clients.NewKeyManagerV1Client() th.AssertNoErr(t, err) diff --git a/acceptance/openstack/keymanager/v1/orders_test.go b/acceptance/openstack/keymanager/v1/orders_test.go index e74ad4d720..cb9095af4f 100644 --- a/acceptance/openstack/keymanager/v1/orders_test.go +++ b/acceptance/openstack/keymanager/v1/orders_test.go @@ -15,9 +15,6 @@ import ( ) func TestOrdersCRUD(t *testing.T) { - clients.SkipRelease(t, "stable/mitaka") - clients.SkipRelease(t, "stable/newton") - clients.SkipRelease(t, "stable/queens") clients.RequireAdmin(t) client, err := clients.NewKeyManagerV1Client() @@ -56,9 +53,6 @@ func TestOrdersCRUD(t *testing.T) { } func TestOrdersAsymmetric(t *testing.T) { - clients.SkipRelease(t, "stable/mitaka") - clients.SkipRelease(t, "stable/newton") - clients.SkipRelease(t, "stable/queens") clients.RequireAdmin(t) client, err := clients.NewKeyManagerV1Client() diff --git a/acceptance/openstack/keymanager/v1/secrets_test.go b/acceptance/openstack/keymanager/v1/secrets_test.go index d46f8e62d5..761ffc1338 100644 --- a/acceptance/openstack/keymanager/v1/secrets_test.go +++ b/acceptance/openstack/keymanager/v1/secrets_test.go @@ -14,10 +14,6 @@ import ( ) func TestSecretsCRUD(t *testing.T) { - clients.SkipRelease(t, "stable/mitaka") - clients.SkipRelease(t, "stable/newton") - clients.SkipRelease(t, "stable/queens") - client, err := clients.NewKeyManagerV1Client() th.AssertNoErr(t, err) @@ -60,10 +56,6 @@ func TestSecretsCRUD(t *testing.T) { } func TestSecretsDelayedPayload(t *testing.T) { - clients.SkipRelease(t, "stable/mitaka") - clients.SkipRelease(t, "stable/newton") - clients.SkipRelease(t, "stable/queens") - client, err := clients.NewKeyManagerV1Client() th.AssertNoErr(t, err) @@ -89,10 +81,6 @@ func TestSecretsDelayedPayload(t *testing.T) { } func TestSecretsMetadataCRUD(t *testing.T) { - clients.SkipRelease(t, "stable/mitaka") - clients.SkipRelease(t, "stable/newton") - clients.SkipRelease(t, "stable/queens") - client, err := clients.NewKeyManagerV1Client() th.AssertNoErr(t, err) @@ -168,10 +156,6 @@ func TestSecretsMetadataCRUD(t *testing.T) { } func TestSymmetricSecret(t *testing.T) { - clients.SkipRelease(t, "stable/mitaka") - clients.SkipRelease(t, "stable/newton") - clients.SkipRelease(t, "stable/queens") - client, err := clients.NewKeyManagerV1Client() th.AssertNoErr(t, err) @@ -187,10 +171,6 @@ func TestSymmetricSecret(t *testing.T) { } func TestCertificateSecret(t *testing.T) { - clients.SkipRelease(t, "stable/mitaka") - clients.SkipRelease(t, "stable/newton") - clients.SkipRelease(t, "stable/queens") - client, err := clients.NewKeyManagerV1Client() th.AssertNoErr(t, err) @@ -210,10 +190,6 @@ func TestCertificateSecret(t *testing.T) { } func TestPrivateSecret(t *testing.T) { - clients.SkipRelease(t, "stable/mitaka") - clients.SkipRelease(t, "stable/newton") - clients.SkipRelease(t, "stable/queens") - client, err := clients.NewKeyManagerV1Client() th.AssertNoErr(t, err) @@ -233,10 +209,6 @@ func TestPrivateSecret(t *testing.T) { } func TestPublicSecret(t *testing.T) { - clients.SkipRelease(t, "stable/mitaka") - clients.SkipRelease(t, "stable/newton") - clients.SkipRelease(t, "stable/queens") - client, err := clients.NewKeyManagerV1Client() th.AssertNoErr(t, err) @@ -255,10 +227,6 @@ func TestPublicSecret(t *testing.T) { } func TestPassphraseSecret(t *testing.T) { - clients.SkipRelease(t, "stable/mitaka") - clients.SkipRelease(t, "stable/newton") - clients.SkipRelease(t, "stable/queens") - client, err := clients.NewKeyManagerV1Client() th.AssertNoErr(t, err) From eacafccd45ce9bb77f33caf308ad797793fa9183 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= <m.andre@redhat.com> Date: Tue, 25 Jan 2022 11:07:41 +0100 Subject: [PATCH 038/360] [CI-v2] Add dns job with Github Action * Deploy Designate with Devstack * Run dns acceptance tests * Logs failures if any --- .github/workflows/functional-dns.yaml | 62 +++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 .github/workflows/functional-dns.yaml diff --git a/.github/workflows/functional-dns.yaml b/.github/workflows/functional-dns.yaml new file mode 100644 index 0000000000..8bb0d4e10e --- /dev/null +++ b/.github/workflows/functional-dns.yaml @@ -0,0 +1,62 @@ +name: functional-dns +on: + pull_request: + paths: + - '^acceptance/openstack/dns.*$' + - '.github/workflows/functional-dns.yaml' + schedule: + - cron: '0 0 * * *' +jobs: + functional-dns: + strategy: + fail-fast: false + matrix: + name: ["master"] + openstack_version: ["master"] + ubuntu_version: ["20.04"] + include: + - name: "xena" + openstack_version: "stable/xena" + ubuntu_version: "20.04" + - name: "wallaby" + openstack_version: "stable/wallaby" + ubuntu_version: "20.04" + - name: "victoria" + openstack_version: "stable/victoria" + ubuntu_version: "20.04" + - name: "ussuri" + openstack_version: "stable/ussuri" + ubuntu_version: "18.04" + - name: "train" + openstack_version: "stable/train" + ubuntu_version: "18.04" + runs-on: ubuntu-${{ matrix.ubuntu_version }} + name: Deploy OpenStack ${{ matrix.name }} with Designate and run dns acceptance tests + steps: + - name: Checkout Gophercloud + uses: actions/checkout@v2 + - name: Deploy devstack + uses: EmilienM/devstack-action@v0.6 + with: + branch: ${{ matrix.openstack_version }} + conf_overrides: | + enable_plugin designate https://opendev.org/openstack/designate ${{ matrix.openstack_version }} + enabled_services: 'designate,designate-central,designate-api,designate-worker,designate-producer,designate-mdns' + - name: Checkout go + uses: actions/setup-go@v2 + with: + go-version: '^1.15' + - name: Run Gophercloud acceptance tests + run: ./script/acceptancetest + env: + DEVSTACK_PATH: ${{ github.workspace }}/devstack + ACCEPTANCE_TESTS_FILTER: "^acceptance/openstack/dns.*$" + - name: Generate logs on failure + run: ./script/collectlogs + if: failure() + - name: Upload logs artifacts on failure + if: failure() + uses: actions/upload-artifact@v2 + with: + name: functional-dns-${{ matrix.name }} + path: /tmp/devstack-logs/* From 02872a534556ad49efdbb96638cb8f9b21e90360 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= <m.andre@redhat.com> Date: Tue, 25 Jan 2022 17:41:45 +0100 Subject: [PATCH 039/360] Remove the RequireDNS check It's not clear what is supposed to set the OS_DNS_ENVIRONMENT environment variable. Looks like it's not coming from devstack. Remove the RequireDNS check as we are now only running these acceptance tests against environment that have DNSaaS anyway. --- acceptance/clients/conditions.go | 8 -------- acceptance/openstack/dns/v2/recordsets_test.go | 4 ---- acceptance/openstack/dns/v2/transfers_test.go | 6 ------ acceptance/openstack/dns/v2/zones_test.go | 2 -- 4 files changed, 20 deletions(-) diff --git a/acceptance/clients/conditions.go b/acceptance/clients/conditions.go index 4a46d07b6f..84c85b7aed 100644 --- a/acceptance/clients/conditions.go +++ b/acceptance/clients/conditions.go @@ -27,14 +27,6 @@ func RequirePortForwarding(t *testing.T) { } } -// RequireDNS will restrict a test to only be run in environments -// that support DNSaaS. -func RequireDNS(t *testing.T) { - if os.Getenv("OS_DNS_ENVIRONMENT") == "" { - t.Skip("this test requires DNSaaS") - } -} - // RequireGuestAgent will restrict a test to only be run in // environments that support the QEMU guest agent. func RequireGuestAgent(t *testing.T) { diff --git a/acceptance/openstack/dns/v2/recordsets_test.go b/acceptance/openstack/dns/v2/recordsets_test.go index d5df5b9334..67b1f706ce 100644 --- a/acceptance/openstack/dns/v2/recordsets_test.go +++ b/acceptance/openstack/dns/v2/recordsets_test.go @@ -14,8 +14,6 @@ import ( ) func TestRecordSetsListByZone(t *testing.T) { - clients.RequireDNS(t) - client, err := clients.NewDNSV2Client() th.AssertNoErr(t, err) @@ -56,8 +54,6 @@ func TestRecordSetsListByZone(t *testing.T) { } func TestRecordSetsCRUD(t *testing.T) { - clients.RequireDNS(t) - client, err := clients.NewDNSV2Client() th.AssertNoErr(t, err) diff --git a/acceptance/openstack/dns/v2/transfers_test.go b/acceptance/openstack/dns/v2/transfers_test.go index e1a783a194..341aec6415 100644 --- a/acceptance/openstack/dns/v2/transfers_test.go +++ b/acceptance/openstack/dns/v2/transfers_test.go @@ -16,8 +16,6 @@ import ( func TestTransferRequestCRUD(t *testing.T) { // Create new Zone - clients.RequireDNS(t) - client, err := clients.NewDNSV2Client() th.AssertNoErr(t, err) @@ -59,8 +57,6 @@ func TestTransferRequestCRUD(t *testing.T) { func TestTransferRequestAccept(t *testing.T) { // Create new project - clients.RequireAdmin(t) - identityClient, err := clients.NewIdentityV3Client() th.AssertNoErr(t, err) @@ -69,8 +65,6 @@ func TestTransferRequestAccept(t *testing.T) { defer identity.DeleteProject(t, identityClient, project.ID) // Create new Zone - clients.RequireDNS(t) - client, err := clients.NewDNSV2Client() th.AssertNoErr(t, err) diff --git a/acceptance/openstack/dns/v2/zones_test.go b/acceptance/openstack/dns/v2/zones_test.go index ad8206265e..e21b96d27c 100644 --- a/acceptance/openstack/dns/v2/zones_test.go +++ b/acceptance/openstack/dns/v2/zones_test.go @@ -13,8 +13,6 @@ import ( ) func TestZonesCRUD(t *testing.T) { - clients.RequireDNS(t) - client, err := clients.NewDNSV2Client() th.AssertNoErr(t, err) From 01ccbcace7a62f29a95be4571e92d07767c40768 Mon Sep 17 00:00:00 2001 From: Emilien Macchi <emilien@redhat.com> Date: Tue, 25 Jan 2022 12:40:01 -0500 Subject: [PATCH 040/360] Github workflows: update REGEX to trigger jobs We thought Github supported usual REGEX syntax but it's not, see: https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#filter-pattern-cheat-sheet So we update the functional workflows so the jobs run when the right files are modified. --- .github/workflows/functional-baremetal.yaml | 3 +-- .github/workflows/functional-blockstorage.yaml | 3 +-- .github/workflows/functional-compute.yaml | 3 +-- .github/workflows/functional-identity.yaml | 3 +-- .github/workflows/functional-imageservice.yaml | 3 +-- .github/workflows/functional-keymanager.yaml | 3 +-- 6 files changed, 6 insertions(+), 12 deletions(-) diff --git a/.github/workflows/functional-baremetal.yaml b/.github/workflows/functional-baremetal.yaml index 3310571b90..26efcddfd1 100644 --- a/.github/workflows/functional-baremetal.yaml +++ b/.github/workflows/functional-baremetal.yaml @@ -2,8 +2,7 @@ name: functional-baremetal on: pull_request: paths: - - '^.*baremetal.*$' - - '.github/workflows/functional-baremetal.yaml' + - '**baremetal**' schedule: - cron: '0 0 * * *' jobs: diff --git a/.github/workflows/functional-blockstorage.yaml b/.github/workflows/functional-blockstorage.yaml index 208808155d..f79c8d0d12 100644 --- a/.github/workflows/functional-blockstorage.yaml +++ b/.github/workflows/functional-blockstorage.yaml @@ -2,8 +2,7 @@ name: functional-blockstorage on: pull_request: paths: - - '^.*blockstorage.*$' - - '.github/workflows/functional-blockstorage.yaml' + - '**blockstorage**' schedule: - cron: '0 0 * * *' jobs: diff --git a/.github/workflows/functional-compute.yaml b/.github/workflows/functional-compute.yaml index fb4f2ed78d..b002b82a68 100644 --- a/.github/workflows/functional-compute.yaml +++ b/.github/workflows/functional-compute.yaml @@ -2,8 +2,7 @@ name: functional-compute on: pull_request: paths: - - '^.*compute.*$' - - '.github/workflows/functional-compute.yaml' + - '**compute**' schedule: - cron: '0 0 * * *' jobs: diff --git a/.github/workflows/functional-identity.yaml b/.github/workflows/functional-identity.yaml index 09b9715e55..5e0f5d8535 100644 --- a/.github/workflows/functional-identity.yaml +++ b/.github/workflows/functional-identity.yaml @@ -2,8 +2,7 @@ name: functional-identity on: pull_request: paths: - - '^.*identity.*$' - - '.github/workflows/functional-identity.yaml' + - '**identity**' schedule: - cron: '0 0 * * *' jobs: diff --git a/.github/workflows/functional-imageservice.yaml b/.github/workflows/functional-imageservice.yaml index 1bcb8bf014..dd24b7af55 100644 --- a/.github/workflows/functional-imageservice.yaml +++ b/.github/workflows/functional-imageservice.yaml @@ -2,8 +2,7 @@ name: functional-imageservice on: pull_request: paths: - - '^.*imageservice.*$' - - '.github/workflows/functional-imageservice.yaml' + - '**imageservice**' schedule: - cron: '0 0 * * *' jobs: diff --git a/.github/workflows/functional-keymanager.yaml b/.github/workflows/functional-keymanager.yaml index 5ea15a1920..200dae116d 100644 --- a/.github/workflows/functional-keymanager.yaml +++ b/.github/workflows/functional-keymanager.yaml @@ -2,8 +2,7 @@ name: functional-keymanager on: pull_request: paths: - - '^.*keymanager.*$' - - '.github/workflows/functional-keymanager.yaml' + - '**keymanager**' schedule: - cron: '0 0 * * *' jobs: From 44bf8e8764153dde6cf9014e852ef7da15a7ec99 Mon Sep 17 00:00:00 2001 From: Emilien Macchi <emilien@redhat.com> Date: Fri, 21 Jan 2022 10:20:23 -0500 Subject: [PATCH 041/360] [CI-v2] Add loadbalancer job with Github Action * Deploy Octavia with Devstack * Run loadbalancer acceptance tests * Introduce IsReleasesAbove and IsReleasesBelow which return True if a release is above the current branch (or below). * Only test tls_versions in Victoria and beyond, not supported before. * Only add l7 parameters to QuotaUpdate in Victoria and beyond, not supported before. * Remove all the skips * Logs failures if any --- .../workflows/functional-loadbalancer.yaml | 63 +++++++++++++++++++ acceptance/clients/conditions.go | 26 ++++++++ .../loadbalancer/v2/amphorae_test.go | 7 --- .../loadbalancer/v2/l7policies_test.go | 7 --- .../loadbalancer/v2/listeners_test.go | 7 --- .../openstack/loadbalancer/v2/loadbalancer.go | 10 ++- .../loadbalancer/v2/loadbalancers_test.go | 47 -------------- .../loadbalancer/v2/monitors_test.go | 7 --- .../openstack/loadbalancer/v2/pools_test.go | 7 --- .../loadbalancer/v2/providers_test.go | 7 --- .../openstack/loadbalancer/v2/quotas.go | 16 ----- .../openstack/loadbalancer/v2/quotas_test.go | 29 +++++++-- 12 files changed, 121 insertions(+), 112 deletions(-) create mode 100644 .github/workflows/functional-loadbalancer.yaml delete mode 100644 acceptance/openstack/loadbalancer/v2/quotas.go diff --git a/.github/workflows/functional-loadbalancer.yaml b/.github/workflows/functional-loadbalancer.yaml new file mode 100644 index 0000000000..a963793453 --- /dev/null +++ b/.github/workflows/functional-loadbalancer.yaml @@ -0,0 +1,63 @@ +name: functional-loadbalancer +on: + pull_request: + paths: + - '**loadbalancer**' + schedule: + - cron: '0 0 * * *' +jobs: + functional-loadbalancer: + strategy: + fail-fast: false + matrix: + name: ["master"] + openstack_version: ["master"] + ubuntu_version: ["20.04"] + include: + - name: "xena" + openstack_version: "stable/xena" + ubuntu_version: "20.04" + - name: "wallaby" + openstack_version: "stable/wallaby" + ubuntu_version: "20.04" + - name: "victoria" + openstack_version: "stable/victoria" + ubuntu_version: "20.04" + - name: "ussuri" + openstack_version: "stable/ussuri" + ubuntu_version: "18.04" + - name: "train" + openstack_version: "stable/train" + ubuntu_version: "18.04" + runs-on: ubuntu-${{ matrix.ubuntu_version }} + name: Deploy OpenStack ${{ matrix.name }} with Octavia and run loadbalancer acceptance tests + steps: + - name: Checkout Gophercloud + uses: actions/checkout@v2 + - name: Deploy devstack + uses: EmilienM/devstack-action@v0.6 + with: + branch: ${{ matrix.openstack_version }} + conf_overrides: | + enable_plugin octavia https://opendev.org/openstack/octavia ${{ matrix.openstack_version }} + enable_plugin neutron https://opendev.org/openstack/neutron ${{ matrix.openstack_version }} + enabled_services: 'octavia,o-api,o-cw,o-hk,o-hm,o-da' + - name: Checkout go + uses: actions/setup-go@v2 + with: + go-version: '^1.15' + - name: Run Gophercloud acceptance tests + run: ./script/acceptancetest + env: + DEVSTACK_PATH: ${{ github.workspace }}/devstack + ACCEPTANCE_TESTS_FILTER: "^.*loadbalancer.*$" + OS_BRANCH: ${{ matrix.openstack_version }} + - name: Generate logs on failure + run: ./script/collectlogs + if: failure() + - name: Upload logs artifacts on failure + if: failure() + uses: actions/upload-artifact@v2 + with: + name: functional-loadbalancer-${{ matrix.name }} + path: /tmp/devstack-logs/* diff --git a/acceptance/clients/conditions.go b/acceptance/clients/conditions.go index 4a46d07b6f..f3c249626a 100644 --- a/acceptance/clients/conditions.go +++ b/acceptance/clients/conditions.go @@ -111,3 +111,29 @@ func SkipReleasesAbove(t *testing.T, release string) { t.Skipf("this is not supported above %s, testing in %s", release, current_branch) } } + +// IsReleasesAbove will return true on releases above a certain +// one. The result is always true on master release. Releases are named such +// as 'stable/mitaka', master, etc. +func IsReleasesAbove(t *testing.T, release string) bool { + current_branch := os.Getenv("OS_BRANCH") + + // Assume master is always too new + if current_branch == "master" || current_branch > release { + return true + } + t.Logf("Target release %s is below the current branch %s", release, current_branch) + return false +} + +// IsReleasesBelow will return true on releases below a certain +// one. Releases are named such as 'stable/mitaka', master, etc. +func IsReleasesBelow(t *testing.T, release string) bool { + current_branch := os.Getenv("OS_BRANCH") + + if current_branch != "master" && current_branch < release { + return true + } + t.Logf("Target release %s is above the current branch %s", release, current_branch) + return false +} diff --git a/acceptance/openstack/loadbalancer/v2/amphorae_test.go b/acceptance/openstack/loadbalancer/v2/amphorae_test.go index b476ea5097..42c14300bb 100644 --- a/acceptance/openstack/loadbalancer/v2/amphorae_test.go +++ b/acceptance/openstack/loadbalancer/v2/amphorae_test.go @@ -13,13 +13,6 @@ import ( func TestAmphoraeList(t *testing.T) { clients.RequireAdmin(t) - clients.SkipRelease(t, "stable/mitaka") - clients.SkipRelease(t, "stable/newton") - clients.SkipRelease(t, "stable/ocata") - clients.SkipRelease(t, "stable/pike") - clients.SkipRelease(t, "stable/queens") - clients.SkipRelease(t, "stable/rocky") - client, err := clients.NewLoadBalancerV2Client() if err != nil { t.Fatalf("Unable to create a loadbalancer client: %v", err) diff --git a/acceptance/openstack/loadbalancer/v2/l7policies_test.go b/acceptance/openstack/loadbalancer/v2/l7policies_test.go index 41de6e9e65..3ad3c9755b 100644 --- a/acceptance/openstack/loadbalancer/v2/l7policies_test.go +++ b/acceptance/openstack/loadbalancer/v2/l7policies_test.go @@ -12,13 +12,6 @@ import ( ) func TestL7PoliciesList(t *testing.T) { - clients.SkipRelease(t, "stable/mitaka") - clients.SkipRelease(t, "stable/newton") - clients.SkipRelease(t, "stable/ocata") - clients.SkipRelease(t, "stable/pike") - clients.SkipRelease(t, "stable/queens") - clients.SkipRelease(t, "stable/rocky") - client, err := clients.NewLoadBalancerV2Client() if err != nil { t.Fatalf("Unable to create a loadbalancer client: %v", err) diff --git a/acceptance/openstack/loadbalancer/v2/listeners_test.go b/acceptance/openstack/loadbalancer/v2/listeners_test.go index 60828cae39..ff06f4af41 100644 --- a/acceptance/openstack/loadbalancer/v2/listeners_test.go +++ b/acceptance/openstack/loadbalancer/v2/listeners_test.go @@ -12,13 +12,6 @@ import ( ) func TestListenersList(t *testing.T) { - clients.SkipRelease(t, "stable/mitaka") - clients.SkipRelease(t, "stable/newton") - clients.SkipRelease(t, "stable/ocata") - clients.SkipRelease(t, "stable/pike") - clients.SkipRelease(t, "stable/queens") - clients.SkipRelease(t, "stable/rocky") - client, err := clients.NewLoadBalancerV2Client() if err != nil { t.Fatalf("Unable to create a loadbalancer client: %v", err) diff --git a/acceptance/openstack/loadbalancer/v2/loadbalancer.go b/acceptance/openstack/loadbalancer/v2/loadbalancer.go index 244d8d90b9..f8d44a8b17 100644 --- a/acceptance/openstack/loadbalancer/v2/loadbalancer.go +++ b/acceptance/openstack/loadbalancer/v2/loadbalancer.go @@ -6,6 +6,7 @@ import ( "testing" "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/l7policies" "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/listeners" @@ -57,6 +58,8 @@ func CreateListener(t *testing.T, client *gophercloud.ServiceClient, lb *loadbal // balancer on a random port with a random name. An error will be returned // if the listener could not be created. func CreateListenerHTTP(t *testing.T, client *gophercloud.ServiceClient, lb *loadbalancers.LoadBalancer) (*listeners.Listener, error) { + tlsVersions := []listeners.TLSVersion{} + tlsVersionsExp := []string(nil) listenerName := tools.RandomString("TESTACCT-", 8) listenerDescription := tools.RandomString("TESTACCT-DESC-", 8) listenerPort := tools.RandomInt(1, 100) @@ -67,8 +70,11 @@ func CreateListenerHTTP(t *testing.T, client *gophercloud.ServiceClient, lb *loa "X-Forwarded-For": "true", } - tlsVersions := []listeners.TLSVersion{"TLSv1.2", "TLSv1.3"} - tlsVersionsExp := []string{"TLSv1.2", "TLSv1.3"} + // tls_version is only supported in microversion v2.17 introduced in victoria + if clients.IsReleasesAbove(t, "stable/ussuri") { + tlsVersions = []listeners.TLSVersion{"TLSv1.2", "TLSv1.3"} + tlsVersionsExp = []string{"TLSv1.2", "TLSv1.3"} + } createOpts := listeners.CreateOpts{ Name: listenerName, diff --git a/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go b/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go index 6d4ba3447e..5be7f88c1a 100644 --- a/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go +++ b/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go @@ -18,13 +18,6 @@ import ( ) func TestLoadbalancersList(t *testing.T) { - clients.SkipRelease(t, "stable/mitaka") - clients.SkipRelease(t, "stable/newton") - clients.SkipRelease(t, "stable/ocata") - clients.SkipRelease(t, "stable/pike") - clients.SkipRelease(t, "stable/queens") - clients.SkipRelease(t, "stable/rocky") - client, err := clients.NewLoadBalancerV2Client() th.AssertNoErr(t, err) @@ -40,14 +33,6 @@ func TestLoadbalancersList(t *testing.T) { } func TestLoadbalancersListByTags(t *testing.T) { - clients.SkipRelease(t, "stable/mitaka") - clients.SkipRelease(t, "stable/newton") - clients.SkipRelease(t, "stable/ocata") - clients.SkipRelease(t, "stable/pike") - clients.SkipRelease(t, "stable/queens") - clients.SkipRelease(t, "stable/rocky") - t.Skip("Currently failing in OpenLab") - netClient, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) @@ -111,14 +96,6 @@ func TestLoadbalancersListByTags(t *testing.T) { } func TestLoadbalancerHTTPCRUD(t *testing.T) { - clients.SkipRelease(t, "stable/mitaka") - clients.SkipRelease(t, "stable/newton") - clients.SkipRelease(t, "stable/ocata") - clients.SkipRelease(t, "stable/pike") - clients.SkipRelease(t, "stable/queens") - clients.SkipRelease(t, "stable/rocky") - t.Skip("Currently failing in OpenLab") - netClient, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) @@ -168,14 +145,6 @@ func TestLoadbalancerHTTPCRUD(t *testing.T) { } func TestLoadbalancersCRUD(t *testing.T) { - clients.SkipRelease(t, "stable/mitaka") - clients.SkipRelease(t, "stable/newton") - clients.SkipRelease(t, "stable/ocata") - clients.SkipRelease(t, "stable/pike") - clients.SkipRelease(t, "stable/queens") - clients.SkipRelease(t, "stable/rocky") - t.Skip("Currently failing in OpenLab") - netClient, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) @@ -475,14 +444,6 @@ func TestLoadbalancersCRUD(t *testing.T) { } func TestLoadbalancersCascadeCRUD(t *testing.T) { - clients.SkipRelease(t, "stable/mitaka") - clients.SkipRelease(t, "stable/newton") - clients.SkipRelease(t, "stable/ocata") - clients.SkipRelease(t, "stable/pike") - clients.SkipRelease(t, "stable/queens") - clients.SkipRelease(t, "stable/rocky") - t.Skip("Currently failing in OpenLab") - netClient, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) @@ -597,14 +558,6 @@ func TestLoadbalancersCascadeCRUD(t *testing.T) { } func TestLoadbalancersFullyPopulatedCRUD(t *testing.T) { - clients.SkipRelease(t, "stable/mitaka") - clients.SkipRelease(t, "stable/newton") - clients.SkipRelease(t, "stable/ocata") - clients.SkipRelease(t, "stable/pike") - clients.SkipRelease(t, "stable/queens") - clients.SkipRelease(t, "stable/rocky") - t.Skip("Currently failing in OpenLab") - netClient, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) diff --git a/acceptance/openstack/loadbalancer/v2/monitors_test.go b/acceptance/openstack/loadbalancer/v2/monitors_test.go index c55d937983..a721f51438 100644 --- a/acceptance/openstack/loadbalancer/v2/monitors_test.go +++ b/acceptance/openstack/loadbalancer/v2/monitors_test.go @@ -12,13 +12,6 @@ import ( ) func TestMonitorsList(t *testing.T) { - clients.SkipRelease(t, "stable/mitaka") - clients.SkipRelease(t, "stable/newton") - clients.SkipRelease(t, "stable/ocata") - clients.SkipRelease(t, "stable/pike") - clients.SkipRelease(t, "stable/queens") - clients.SkipRelease(t, "stable/rocky") - client, err := clients.NewLoadBalancerV2Client() if err != nil { t.Fatalf("Unable to create a loadbalancer client: %v", err) diff --git a/acceptance/openstack/loadbalancer/v2/pools_test.go b/acceptance/openstack/loadbalancer/v2/pools_test.go index 2f8b39b036..8a9724dd94 100644 --- a/acceptance/openstack/loadbalancer/v2/pools_test.go +++ b/acceptance/openstack/loadbalancer/v2/pools_test.go @@ -12,13 +12,6 @@ import ( ) func TestPoolsList(t *testing.T) { - clients.SkipRelease(t, "stable/mitaka") - clients.SkipRelease(t, "stable/newton") - clients.SkipRelease(t, "stable/ocata") - clients.SkipRelease(t, "stable/pike") - clients.SkipRelease(t, "stable/queens") - clients.SkipRelease(t, "stable/rocky") - client, err := clients.NewLoadBalancerV2Client() if err != nil { t.Fatalf("Unable to create a loadbalancer client: %v", err) diff --git a/acceptance/openstack/loadbalancer/v2/providers_test.go b/acceptance/openstack/loadbalancer/v2/providers_test.go index 7c1e42217d..c65511bbf9 100644 --- a/acceptance/openstack/loadbalancer/v2/providers_test.go +++ b/acceptance/openstack/loadbalancer/v2/providers_test.go @@ -12,13 +12,6 @@ import ( ) func TestProvidersList(t *testing.T) { - clients.SkipRelease(t, "stable/mitaka") - clients.SkipRelease(t, "stable/newton") - clients.SkipRelease(t, "stable/ocata") - clients.SkipRelease(t, "stable/pike") - clients.SkipRelease(t, "stable/queens") - clients.SkipRelease(t, "stable/rocky") - client, err := clients.NewLoadBalancerV2Client() if err != nil { t.Fatalf("Unable to create a loadbalancer client: %v", err) diff --git a/acceptance/openstack/loadbalancer/v2/quotas.go b/acceptance/openstack/loadbalancer/v2/quotas.go deleted file mode 100644 index 9a0819e46e..0000000000 --- a/acceptance/openstack/loadbalancer/v2/quotas.go +++ /dev/null @@ -1,16 +0,0 @@ -package v2 - -import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/quotas" -) - -var quotaUpdateOpts = quotas.UpdateOpts{ - Loadbalancer: gophercloud.IntToPointer(25), - Listener: gophercloud.IntToPointer(45), - Member: gophercloud.IntToPointer(205), - Pool: gophercloud.IntToPointer(25), - Healthmonitor: gophercloud.IntToPointer(5), - L7Policy: gophercloud.IntToPointer(55), - L7Rule: gophercloud.IntToPointer(105), -} diff --git a/acceptance/openstack/loadbalancer/v2/quotas_test.go b/acceptance/openstack/loadbalancer/v2/quotas_test.go index a309dbb62b..4e174642c4 100644 --- a/acceptance/openstack/loadbalancer/v2/quotas_test.go +++ b/acceptance/openstack/loadbalancer/v2/quotas_test.go @@ -9,6 +9,7 @@ import ( "reflect" "testing" + "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/quotas" @@ -36,6 +37,19 @@ func TestQuotasUpdate(t *testing.T) { originalQuotas, err := quotas.Get(client, os.Getenv("OS_PROJECT_NAME")).Extract() th.AssertNoErr(t, err) + var quotaUpdateOpts = quotas.UpdateOpts{ + Loadbalancer: gophercloud.IntToPointer(25), + Listener: gophercloud.IntToPointer(45), + Member: gophercloud.IntToPointer(205), + Pool: gophercloud.IntToPointer(25), + Healthmonitor: gophercloud.IntToPointer(5), + } + // L7 parameters are only supported in microversion v2.19 introduced in victoria + if clients.IsReleasesAbove(t, "stable/ussuri") { + quotaUpdateOpts.L7Policy = gophercloud.IntToPointer(55) + quotaUpdateOpts.L7Rule = gophercloud.IntToPointer(105) + } + newQuotas, err := quotas.Update(client, os.Getenv("OS_PROJECT_NAME"), quotaUpdateOpts).Extract() th.AssertNoErr(t, err) @@ -45,16 +59,21 @@ func TestQuotasUpdate(t *testing.T) { log.Fatal("Original and New Loadbalancer Quotas are the same") } - // Restore original quotas. - restoredQuotas, err := quotas.Update(client, os.Getenv("OS_PROJECT_NAME"), quotas.UpdateOpts{ + var restoredQuotaUpdate = quotas.UpdateOpts{ Loadbalancer: &originalQuotas.Loadbalancer, Listener: &originalQuotas.Listener, Member: &originalQuotas.Member, Pool: &originalQuotas.Pool, Healthmonitor: &originalQuotas.Healthmonitor, - L7Policy: &originalQuotas.L7Policy, - L7Rule: &originalQuotas.L7Rule, - }).Extract() + } + // L7 parameters are only supported in microversion v2.19 introduced in victoria + if clients.IsReleasesAbove(t, "stable/ussuri") { + restoredQuotaUpdate.L7Policy = &originalQuotas.L7Policy + restoredQuotaUpdate.L7Rule = &originalQuotas.L7Rule + } + + // Restore original quotas. + restoredQuotas, err := quotas.Update(client, os.Getenv("OS_PROJECT_NAME"), restoredQuotaUpdate).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, originalQuotas, restoredQuotas) From f232e76144f0ed51088e81d7d24f587b0fe9aaf9 Mon Sep 17 00:00:00 2001 From: Emilien Macchi <emilien@redhat.com> Date: Tue, 11 Jan 2022 16:35:46 -0500 Subject: [PATCH 042/360] [CI-v2] Add networking job with Github Action This runs a Github Action to: * Deploy Devstack with Neutron * Run acceptance/openstack/networking * Unskip a lot of tests * Do not test FWaaS v1, too old and will be removed from Gophercloud via #2320 * Skip FWAAS v2 testing after Ussuri (service was removed); and deploy it only on Ussuri and Train, so it can still be tested. * Fix a test for FWAAS, where ICMP rule can't have a port in source and source/dest have to be comma separated ranges. * Skip TestAgentsRUD because it can't work with OVN (default in devstack now). This test will have to be reworked in the future. * Fix TestDNSPortCRUDL test (some typos) * Skip TestLayer3RouterAgents because it can't work with OVN (default in devstack now). This test will have to be rewored in the future. * Skip TestDNSFloatingIPCRDL which doesn't work with ML2/OVN * Skip LBAAS (v1 and v2) from Neutron API, it was removed and replaced by Octavia. We'll need to deprecate and remove it from Gophercloud as well. * In TestMTUNetworkCRUDL, reduce the MTU to 1440. With OVN + Geneve, we can't go above 1442, so let's set 1440 for the test to be safe. * Skip VlanTransparent, Trunk and QoS testing if the extension is not there. * Post logs if we encounter a failure --- .github/workflows/functional-networking.yaml | 72 +++++++++++++++++++ .../v2/extensions/agents/agents_test.go | 4 +- .../v2/extensions/attributestags_test.go | 2 - .../networking/v2/extensions/dns/dns.go | 6 +- .../networking/v2/extensions/dns/dns_test.go | 1 + .../v2/extensions/fwaas/firewall_test.go | 3 + .../v2/extensions/fwaas/policy_test.go | 1 + .../v2/extensions/fwaas/rule_test.go | 1 + .../v2/extensions/fwaas_v2/fwaas_v2.go | 10 +-- .../v2/extensions/fwaas_v2/groups_test.go | 1 + .../v2/extensions/fwaas_v2/policy_test.go | 2 + .../v2/extensions/fwaas_v2/rule_test.go | 9 ++- .../v2/extensions/layer3/routers_test.go | 1 + .../v2/extensions/lbaas/members_test.go | 2 + .../v2/extensions/lbaas/monitors_test.go | 2 + .../v2/extensions/lbaas/pools_test.go | 3 + .../v2/extensions/lbaas/vips_test.go | 2 + .../v2/extensions/lbaas_v2/l7policies_test.go | 1 + .../v2/extensions/lbaas_v2/listeners_test.go | 1 + .../extensions/lbaas_v2/loadbalancers_test.go | 2 + .../v2/extensions/lbaas_v2/monitors_test.go | 1 + .../v2/extensions/lbaas_v2/pools_test.go | 1 + .../networking/v2/extensions/mtu/mtu_test.go | 5 +- .../extensions/qos/policies/policies_test.go | 7 ++ .../v2/extensions/qos/rules/rules_test.go | 23 ++++-- .../qos/ruletypes/ruletypes_test.go | 11 +-- .../v2/extensions/trunks/trunks_test.go | 39 ++++++---- .../vlantransparent/vlantransparent_test.go | 9 ++- .../v2/extensions/vpnaas/service_test.go | 1 + .../extensions/vpnaas/siteconnection_test.go | 1 + 30 files changed, 183 insertions(+), 41 deletions(-) create mode 100644 .github/workflows/functional-networking.yaml diff --git a/.github/workflows/functional-networking.yaml b/.github/workflows/functional-networking.yaml new file mode 100644 index 0000000000..7c2f946359 --- /dev/null +++ b/.github/workflows/functional-networking.yaml @@ -0,0 +1,72 @@ +name: functional-networking +on: + pull_request: + paths: + - '**networking**' + schedule: + - cron: '0 0 * * *' +jobs: + functional-networking: + strategy: + fail-fast: false + matrix: + name: ["master"] + openstack_version: ["master"] + ubuntu_version: ["20.04"] + devstack_conf_overrides: [""] + include: + - name: "xena" + openstack_version: "stable/xena" + ubuntu_version: "20.04" + devstack_conf_overrides: "" + - name: "wallaby" + openstack_version: "stable/wallaby" + ubuntu_version: "20.04" + devstack_conf_overrides: "" + - name: "victoria" + openstack_version: "stable/victoria" + ubuntu_version: "20.04" + devstack_conf_overrides: "" + - name: "ussuri" + openstack_version: "stable/ussuri" + ubuntu_version: "18.04" + devstack_conf_overrides: | + enable_plugin neutron-fwaas https://git.openstack.org/openstack/neutron-fwaas stable/ussuri + - name: "train" + openstack_version: "stable/train" + ubuntu_version: "18.04" + devstack_conf_overrides: | + enable_plugin neutron-fwaas https://git.openstack.org/openstack/neutron-fwaas stable/train + runs-on: ubuntu-${{ matrix.ubuntu_version }} + name: Deploy OpenStack ${{ matrix.name }} with Neutron and run networking acceptance tests + steps: + - name: Checkout Gophercloud + uses: actions/checkout@v2 + - name: Deploy devstack + uses: EmilienM/devstack-action@v0.6 + with: + branch: ${{ matrix.openstack_version }} + conf_overrides: | + Q_ML2_PLUGIN_EXT_DRIVERS=qos,port_security,dns_domain_keywords + enable_plugin neutron-vpnaas https://opendev.org/openstack/neutron-vpnaas ${{ matrix.openstack_version }} + ${{ matrix.devstack_conf_overrides }} + enabled_services: 'neutron-dns,neutron-qos,neutron-segments,neutron-trunk,neutron-uplink-status-propagation,neutron-network-segment-range,neutron-port-forwarding' + - name: Checkout go + uses: actions/setup-go@v2 + with: + go-version: '^1.15' + - name: Run Gophercloud acceptance tests + run: ./script/acceptancetest + env: + DEVSTACK_PATH: ${{ github.workspace }}/devstack + ACCEPTANCE_TESTS_FILTER: "^.*networking.*$" + OS_BRANCH: ${{ matrix.openstack_version }} + - name: Generate logs on failure + run: ./script/collectlogs + if: failure() + - name: Upload logs artifacts on failure + if: failure() + uses: actions/upload-artifact@v2 + with: + name: functional-networking-${{ matrix.name }} + path: /tmp/devstack-logs/* diff --git a/acceptance/openstack/networking/v2/extensions/agents/agents_test.go b/acceptance/openstack/networking/v2/extensions/agents/agents_test.go index c97a50e69d..3be89554b8 100644 --- a/acceptance/openstack/networking/v2/extensions/agents/agents_test.go +++ b/acceptance/openstack/networking/v2/extensions/agents/agents_test.go @@ -14,6 +14,7 @@ import ( ) func TestAgentsRUD(t *testing.T) { + t.Skip("TestAgentsRUD needs to be re-worked to work with both ML2/OVS and OVN") clients.RequireAdmin(t) client, err := clients.NewNetworkV2Client() @@ -70,9 +71,6 @@ func TestAgentsRUD(t *testing.T) { th.AssertNoErr(t, err) th.AssertEquals(t, agent.Description, allAgents[0].Description) - // skip this part - // t.Skip("Skip DHCP agent network scheduling") - // Assign a new network to a DHCP agent network, err := networking.CreateNetwork(t, client) th.AssertNoErr(t, err) diff --git a/acceptance/openstack/networking/v2/extensions/attributestags_test.go b/acceptance/openstack/networking/v2/extensions/attributestags_test.go index 573b181853..b30a10e894 100644 --- a/acceptance/openstack/networking/v2/extensions/attributestags_test.go +++ b/acceptance/openstack/networking/v2/extensions/attributestags_test.go @@ -89,8 +89,6 @@ func listNetworkWithTagOpts(t *testing.T, client *gophercloud.ServiceClient, lis } func TestQueryByTags(t *testing.T) { - clients.SkipRelease(t, "stable/mitaka") - client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) diff --git a/acceptance/openstack/networking/v2/extensions/dns/dns.go b/acceptance/openstack/networking/v2/extensions/dns/dns.go index c625d68be9..e6f5b2e20e 100644 --- a/acceptance/openstack/networking/v2/extensions/dns/dns.go +++ b/acceptance/openstack/networking/v2/extensions/dns/dns.go @@ -105,7 +105,7 @@ func CreateFloatingIPDNS(t *testing.T, client *gophercloud.ServiceClient, networ // CreateNetworkDNS will create a network with a DNS domain set. // An error will be returned if the network could not be created. -func CreateNetworkDNS(t *testing.T, client *gophercloud.ServiceClient, dnsDomanin string) (*NetworkWithDNSExt, error) { +func CreateNetworkDNS(t *testing.T, client *gophercloud.ServiceClient, dnsDomain string) (*NetworkWithDNSExt, error) { networkName := tools.RandomString("TESTACC-", 8) networkCreateOpts := networks.CreateOpts{ Name: networkName, @@ -114,7 +114,7 @@ func CreateNetworkDNS(t *testing.T, client *gophercloud.ServiceClient, dnsDomani createOpts := dns.NetworkCreateOptsExt{ CreateOptsBuilder: networkCreateOpts, - DNSDomain: dnsDomanin, + DNSDomain: dnsDomain, } t.Logf("Attempting to create network: %s", networkName) @@ -129,7 +129,7 @@ func CreateNetworkDNS(t *testing.T, client *gophercloud.ServiceClient, dnsDomani t.Logf("Successfully created network.") th.AssertEquals(t, network.Name, networkName) - th.AssertEquals(t, network.DNSDomain, dnsDomanin) + th.AssertEquals(t, network.DNSDomain, dnsDomain) return &network, nil } diff --git a/acceptance/openstack/networking/v2/extensions/dns/dns_test.go b/acceptance/openstack/networking/v2/extensions/dns/dns_test.go index 0a620bb579..005d470b23 100644 --- a/acceptance/openstack/networking/v2/extensions/dns/dns_test.go +++ b/acceptance/openstack/networking/v2/extensions/dns/dns_test.go @@ -147,6 +147,7 @@ func TestDNSPortCRUDL(t *testing.T) { } func TestDNSFloatingIPCRDL(t *testing.T) { + t.Skip("Skipping TestDNSFloatingIPCRDL for now, as it doesn't work with ML2/OVN.") clients.RequireAdmin(t) client, err := clients.NewNetworkV2Client() diff --git a/acceptance/openstack/networking/v2/extensions/fwaas/firewall_test.go b/acceptance/openstack/networking/v2/extensions/fwaas/firewall_test.go index 1af25efa43..95533af241 100644 --- a/acceptance/openstack/networking/v2/extensions/fwaas/firewall_test.go +++ b/acceptance/openstack/networking/v2/extensions/fwaas/firewall_test.go @@ -15,6 +15,7 @@ import ( ) func TestFirewallCRUD(t *testing.T) { + t.Skip("Skip this test, FWAAS v1 is old and will be removed from Gophercloud") client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) @@ -76,6 +77,7 @@ func TestFirewallCRUD(t *testing.T) { } func TestFirewallCRUDRouter(t *testing.T) { + t.Skip("Skip this test, FWAAS v1 is old and will be removed from Gophercloud") client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) @@ -126,6 +128,7 @@ func TestFirewallCRUDRouter(t *testing.T) { } func TestFirewallCRUDRemoveRouter(t *testing.T) { + t.Skip("Skip this test, FWAAS v1 is old and will be removed from Gophercloud") client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) diff --git a/acceptance/openstack/networking/v2/extensions/fwaas/policy_test.go b/acceptance/openstack/networking/v2/extensions/fwaas/policy_test.go index b075e794be..a10839b8f4 100644 --- a/acceptance/openstack/networking/v2/extensions/fwaas/policy_test.go +++ b/acceptance/openstack/networking/v2/extensions/fwaas/policy_test.go @@ -13,6 +13,7 @@ import ( ) func TestPolicyCRUD(t *testing.T) { + t.Skip("Skip this test, FWAAS v1 is old and will be removed from Gophercloud") client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) diff --git a/acceptance/openstack/networking/v2/extensions/fwaas/rule_test.go b/acceptance/openstack/networking/v2/extensions/fwaas/rule_test.go index be32806c96..343211990e 100644 --- a/acceptance/openstack/networking/v2/extensions/fwaas/rule_test.go +++ b/acceptance/openstack/networking/v2/extensions/fwaas/rule_test.go @@ -13,6 +13,7 @@ import ( ) func TestRuleCRUD(t *testing.T) { + t.Skip("Skip this test, FWAAS v1 is old and will be removed from Gophercloud") client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) diff --git a/acceptance/openstack/networking/v2/extensions/fwaas_v2/fwaas_v2.go b/acceptance/openstack/networking/v2/extensions/fwaas_v2/fwaas_v2.go index ce3bdbbbb6..1b225691ff 100644 --- a/acceptance/openstack/networking/v2/extensions/fwaas_v2/fwaas_v2.go +++ b/acceptance/openstack/networking/v2/extensions/fwaas_v2/fwaas_v2.go @@ -74,9 +74,11 @@ func CreatePolicy(t *testing.T, client *gophercloud.ServiceClient, ruleID string func CreateRule(t *testing.T, client *gophercloud.ServiceClient) (*rules.Rule, error) { ruleName := tools.RandomString("TESTACC-", 8) sourceAddress := fmt.Sprintf("192.168.1.%d", tools.RandomInt(1, 100)) - sourcePort := strconv.Itoa(tools.RandomInt(1, 100)) + sourcePortInt := strconv.Itoa(tools.RandomInt(1, 100)) + sourcePort := fmt.Sprintf("%s:%s", sourcePortInt, sourcePortInt) destinationAddress := fmt.Sprintf("192.168.2.%d", tools.RandomInt(1, 100)) - destinationPort := strconv.Itoa(tools.RandomInt(1, 100)) + destinationPortInt := strconv.Itoa(tools.RandomInt(1, 100)) + destinationPort := fmt.Sprintf("%s:%s", destinationPortInt, destinationPortInt) t.Logf("Attempting to create rule %s with source %s:%s and destination %s:%s", ruleName, sourceAddress, sourcePort, destinationAddress, destinationPort) @@ -102,9 +104,9 @@ func CreateRule(t *testing.T, client *gophercloud.ServiceClient) (*rules.Rule, e th.AssertEquals(t, rule.Protocol, string(rules.ProtocolTCP)) th.AssertEquals(t, rule.Action, string(rules.ActionAllow)) th.AssertEquals(t, rule.SourceIPAddress, sourceAddress) - th.AssertEquals(t, rule.SourcePort, sourcePort) + th.AssertEquals(t, rule.SourcePort, sourcePortInt) th.AssertEquals(t, rule.DestinationIPAddress, destinationAddress) - th.AssertEquals(t, rule.DestinationPort, destinationPort) + th.AssertEquals(t, rule.DestinationPort, destinationPortInt) return rule, nil } diff --git a/acceptance/openstack/networking/v2/extensions/fwaas_v2/groups_test.go b/acceptance/openstack/networking/v2/extensions/fwaas_v2/groups_test.go index a6a0bcc36e..8dc8518626 100644 --- a/acceptance/openstack/networking/v2/extensions/fwaas_v2/groups_test.go +++ b/acceptance/openstack/networking/v2/extensions/fwaas_v2/groups_test.go @@ -13,6 +13,7 @@ import ( ) func TestGroupCRUD(t *testing.T) { + clients.SkipReleasesAbove(t, "stable/ussuri") client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) diff --git a/acceptance/openstack/networking/v2/extensions/fwaas_v2/policy_test.go b/acceptance/openstack/networking/v2/extensions/fwaas_v2/policy_test.go index 2a86e78b6e..45a1c9860f 100644 --- a/acceptance/openstack/networking/v2/extensions/fwaas_v2/policy_test.go +++ b/acceptance/openstack/networking/v2/extensions/fwaas_v2/policy_test.go @@ -13,6 +13,8 @@ import ( ) func TestPolicyCRUD(t *testing.T) { + clients.SkipReleasesAbove(t, "stable/ussuri") + client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) diff --git a/acceptance/openstack/networking/v2/extensions/fwaas_v2/rule_test.go b/acceptance/openstack/networking/v2/extensions/fwaas_v2/rule_test.go index f9db68d5ae..36c9ea720c 100644 --- a/acceptance/openstack/networking/v2/extensions/fwaas_v2/rule_test.go +++ b/acceptance/openstack/networking/v2/extensions/fwaas_v2/rule_test.go @@ -4,6 +4,8 @@ package fwaas_v2 import ( + "fmt" + "strconv" "testing" "github.com/gophercloud/gophercloud/acceptance/clients" @@ -13,6 +15,7 @@ import ( ) func TestRuleCRUD(t *testing.T) { + clients.SkipReleasesAbove(t, "stable/ussuri") client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) @@ -24,15 +27,19 @@ func TestRuleCRUD(t *testing.T) { tools.PrintResource(t, rule) ruleDescription := "Some rule description" - ruleProtocol := rules.ProtocolICMP + ruleSourcePortInt := strconv.Itoa(tools.RandomInt(1, 100)) + ruleSourcePort := fmt.Sprintf("%s:%s", ruleSourcePortInt, ruleSourcePortInt) + ruleProtocol := rules.ProtocolTCP updateOpts := rules.UpdateOpts{ Description: &ruleDescription, Protocol: &ruleProtocol, + SourcePort: &ruleSourcePort, } ruleUpdated, err := rules.Update(client, rule.ID, updateOpts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, ruleUpdated.Description, ruleDescription) + th.AssertEquals(t, ruleUpdated.SourcePort, ruleSourcePortInt) th.AssertEquals(t, ruleUpdated.Protocol, string(ruleProtocol)) newRule, err := rules.Get(client, rule.ID).Extract() diff --git a/acceptance/openstack/networking/v2/extensions/layer3/routers_test.go b/acceptance/openstack/networking/v2/extensions/layer3/routers_test.go index bf26bb44e4..c4bb5eb40e 100644 --- a/acceptance/openstack/networking/v2/extensions/layer3/routers_test.go +++ b/acceptance/openstack/networking/v2/extensions/layer3/routers_test.go @@ -167,6 +167,7 @@ func TestLayer3RouterInterface(t *testing.T) { } func TestLayer3RouterAgents(t *testing.T) { + t.Skip("TestLayer3RouterAgents needs to be re-worked to work with both ML2/OVS and OVN") clients.RequireAdmin(t) client, err := clients.NewNetworkV2Client() diff --git a/acceptance/openstack/networking/v2/extensions/lbaas/members_test.go b/acceptance/openstack/networking/v2/extensions/lbaas/members_test.go index 7770dbf422..78be8832d4 100644 --- a/acceptance/openstack/networking/v2/extensions/lbaas/members_test.go +++ b/acceptance/openstack/networking/v2/extensions/lbaas/members_test.go @@ -14,6 +14,7 @@ import ( ) func TestMembersList(t *testing.T) { + t.Skip("Neutron LBaaS was replaced by Octavia and the API will be removed in a future release") client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) @@ -35,6 +36,7 @@ func TestMembersList(t *testing.T) { } func TestMembersCRUD(t *testing.T) { + t.Skip("Neutron LBaaS was replaced by Octavia and the API will be removed in a future release") client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) diff --git a/acceptance/openstack/networking/v2/extensions/lbaas/monitors_test.go b/acceptance/openstack/networking/v2/extensions/lbaas/monitors_test.go index e2027414a6..27ea62158b 100644 --- a/acceptance/openstack/networking/v2/extensions/lbaas/monitors_test.go +++ b/acceptance/openstack/networking/v2/extensions/lbaas/monitors_test.go @@ -12,6 +12,7 @@ import ( ) func TestMonitorsList(t *testing.T) { + t.Skip("Neutron LBaaS was replaced by Octavia and the API will be removed in a future release") client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) @@ -33,6 +34,7 @@ func TestMonitorsList(t *testing.T) { } func TestMonitorsCRUD(t *testing.T) { + t.Skip("Neutron LBaaS was replaced by Octavia and the API will be removed in a future release") client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) diff --git a/acceptance/openstack/networking/v2/extensions/lbaas/pools_test.go b/acceptance/openstack/networking/v2/extensions/lbaas/pools_test.go index 82a49056a7..0c8ba65d0c 100644 --- a/acceptance/openstack/networking/v2/extensions/lbaas/pools_test.go +++ b/acceptance/openstack/networking/v2/extensions/lbaas/pools_test.go @@ -13,6 +13,7 @@ import ( ) func TestPoolsList(t *testing.T) { + t.Skip("Neutron LBaaS was replaced by Octavia and the API will be removed in a future release") client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) @@ -34,6 +35,7 @@ func TestPoolsList(t *testing.T) { } func TestPoolsCRUD(t *testing.T) { + t.Skip("Neutron LBaaS was replaced by Octavia and the API will be removed in a future release") client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) @@ -77,6 +79,7 @@ func TestPoolsCRUD(t *testing.T) { } func TestPoolsMonitors(t *testing.T) { + t.Skip("Neutron LBaaS was replaced by Octavia and the API will be removed in a future release") client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) diff --git a/acceptance/openstack/networking/v2/extensions/lbaas/vips_test.go b/acceptance/openstack/networking/v2/extensions/lbaas/vips_test.go index b691191943..a954f25e28 100644 --- a/acceptance/openstack/networking/v2/extensions/lbaas/vips_test.go +++ b/acceptance/openstack/networking/v2/extensions/lbaas/vips_test.go @@ -13,6 +13,7 @@ import ( ) func TestVIPsList(t *testing.T) { + t.Skip("Neutron LBaaS was replaced by Octavia and the API will be removed in a future release") client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) @@ -34,6 +35,7 @@ func TestVIPsList(t *testing.T) { } func TestVIPsCRUD(t *testing.T) { + t.Skip("Neutron LBaaS was replaced by Octavia and the API will be removed in a future release") client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) diff --git a/acceptance/openstack/networking/v2/extensions/lbaas_v2/l7policies_test.go b/acceptance/openstack/networking/v2/extensions/lbaas_v2/l7policies_test.go index 85a232b7cf..aa69b9e448 100644 --- a/acceptance/openstack/networking/v2/extensions/lbaas_v2/l7policies_test.go +++ b/acceptance/openstack/networking/v2/extensions/lbaas_v2/l7policies_test.go @@ -12,6 +12,7 @@ import ( ) func TestL7PoliciesList(t *testing.T) { + t.Skip("Neutron LBaaS v2 was replaced by Octavia and the API will be removed in a future release") client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a loadbalancer client: %v", err) diff --git a/acceptance/openstack/networking/v2/extensions/lbaas_v2/listeners_test.go b/acceptance/openstack/networking/v2/extensions/lbaas_v2/listeners_test.go index cc530d0dde..72d4b72282 100644 --- a/acceptance/openstack/networking/v2/extensions/lbaas_v2/listeners_test.go +++ b/acceptance/openstack/networking/v2/extensions/lbaas_v2/listeners_test.go @@ -12,6 +12,7 @@ import ( ) func TestListenersList(t *testing.T) { + t.Skip("Neutron LBaaS v2 was replaced by Octavia and the API will be removed in a future release") client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a loadbalancer client: %v", err) diff --git a/acceptance/openstack/networking/v2/extensions/lbaas_v2/loadbalancers_test.go b/acceptance/openstack/networking/v2/extensions/lbaas_v2/loadbalancers_test.go index 061a30ef59..8529deb957 100644 --- a/acceptance/openstack/networking/v2/extensions/lbaas_v2/loadbalancers_test.go +++ b/acceptance/openstack/networking/v2/extensions/lbaas_v2/loadbalancers_test.go @@ -18,6 +18,7 @@ import ( ) func TestLoadbalancersList(t *testing.T) { + t.Skip("Neutron LBaaS v2 was replaced by Octavia and the API will be removed in a future release") client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) @@ -33,6 +34,7 @@ func TestLoadbalancersList(t *testing.T) { } func TestLoadbalancersCRUD(t *testing.T) { + t.Skip("Neutron LBaaS v2 was replaced by Octavia and the API will be removed in a future release") client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) diff --git a/acceptance/openstack/networking/v2/extensions/lbaas_v2/monitors_test.go b/acceptance/openstack/networking/v2/extensions/lbaas_v2/monitors_test.go index 37125ee088..18e145bc90 100644 --- a/acceptance/openstack/networking/v2/extensions/lbaas_v2/monitors_test.go +++ b/acceptance/openstack/networking/v2/extensions/lbaas_v2/monitors_test.go @@ -12,6 +12,7 @@ import ( ) func TestMonitorsList(t *testing.T) { + t.Skip("Neutron LBaaS v2 was replaced by Octavia and the API will be removed in a future release") client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a loadbalancer client: %v", err) diff --git a/acceptance/openstack/networking/v2/extensions/lbaas_v2/pools_test.go b/acceptance/openstack/networking/v2/extensions/lbaas_v2/pools_test.go index 43ba94b67f..69c4f6e1fc 100644 --- a/acceptance/openstack/networking/v2/extensions/lbaas_v2/pools_test.go +++ b/acceptance/openstack/networking/v2/extensions/lbaas_v2/pools_test.go @@ -12,6 +12,7 @@ import ( ) func TestPoolsList(t *testing.T) { + t.Skip("Neutron LBaaS v2 was replaced by Octavia and the API will be removed in a future release") client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a loadbalancer client: %v", err) diff --git a/acceptance/openstack/networking/v2/extensions/mtu/mtu_test.go b/acceptance/openstack/networking/v2/extensions/mtu/mtu_test.go index 21dfc56feb..51b32a334d 100644 --- a/acceptance/openstack/networking/v2/extensions/mtu/mtu_test.go +++ b/acceptance/openstack/networking/v2/extensions/mtu/mtu_test.go @@ -18,9 +18,6 @@ import ( func TestMTUNetworkCRUDL(t *testing.T) { clients.RequireAdmin(t) - clients.SkipRelease(t, "stable/mitaka") - clients.SkipRelease(t, "stable/newton") - clients.SkipRelease(t, "stable/ocata") client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) @@ -37,7 +34,7 @@ func TestMTUNetworkCRUDL(t *testing.T) { // Create Network var networkMTU int if mtuWritable != nil { - networkMTU = 1449 + networkMTU = 1440 } network, err := CreateNetworkWithMTU(t, client, &networkMTU) th.AssertNoErr(t, err) diff --git a/acceptance/openstack/networking/v2/extensions/qos/policies/policies_test.go b/acceptance/openstack/networking/v2/extensions/qos/policies/policies_test.go index ae1fd190e2..7dcbd68f1c 100644 --- a/acceptance/openstack/networking/v2/extensions/qos/policies/policies_test.go +++ b/acceptance/openstack/networking/v2/extensions/qos/policies/policies_test.go @@ -8,6 +8,7 @@ import ( "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/common/extensions" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/qos/policies" th "github.com/gophercloud/gophercloud/testhelper" ) @@ -16,6 +17,12 @@ func TestPoliciesCRUD(t *testing.T) { client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) + extension, err := extensions.Get(client, "qos").Extract() + if err != nil { + t.Skip("This test requires qos Neutron extension") + } + tools.PrintResource(t, extension) + // Create a QoS policy. policy, err := CreateQoSPolicy(t, client) th.AssertNoErr(t, err) diff --git a/acceptance/openstack/networking/v2/extensions/qos/rules/rules_test.go b/acceptance/openstack/networking/v2/extensions/qos/rules/rules_test.go index b9b451fb59..43cfaaedea 100644 --- a/acceptance/openstack/networking/v2/extensions/qos/rules/rules_test.go +++ b/acceptance/openstack/networking/v2/extensions/qos/rules/rules_test.go @@ -6,6 +6,7 @@ import ( "github.com/gophercloud/gophercloud/acceptance/clients" accpolicies "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2/extensions/qos/policies" "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/common/extensions" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/qos/policies" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/qos/rules" th "github.com/gophercloud/gophercloud/testhelper" @@ -15,6 +16,12 @@ func TestBandwidthLimitRulesCRUD(t *testing.T) { client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) + extension, err := extensions.Get(client, "qos").Extract() + if err != nil { + t.Skip("This test requires qos Neutron extension") + } + tools.PrintResource(t, extension) + // Create a QoS policy policy, err := accpolicies.CreateQoSPolicy(t, client) th.AssertNoErr(t, err) @@ -55,11 +62,15 @@ func TestBandwidthLimitRulesCRUD(t *testing.T) { } func TestDSCPMarkingRulesCRUD(t *testing.T) { - clients.SkipRelease(t, "stable/mitaka") - client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) + extension, err := extensions.Get(client, "qos").Extract() + if err != nil { + t.Skip("This test requires qos Neutron extension") + } + tools.PrintResource(t, extension) + // Create a QoS policy policy, err := accpolicies.CreateQoSPolicy(t, client) th.AssertNoErr(t, err) @@ -100,11 +111,15 @@ func TestDSCPMarkingRulesCRUD(t *testing.T) { } func TestMinimumBandwidthRulesCRUD(t *testing.T) { - clients.SkipRelease(t, "stable/mitaka") - client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) + extension, err := extensions.Get(client, "qos").Extract() + if err != nil { + t.Skip("This test requires qos Neutron extension") + } + tools.PrintResource(t, extension) + // Create a QoS policy policy, err := accpolicies.CreateQoSPolicy(t, client) th.AssertNoErr(t, err) diff --git a/acceptance/openstack/networking/v2/extensions/qos/ruletypes/ruletypes_test.go b/acceptance/openstack/networking/v2/extensions/qos/ruletypes/ruletypes_test.go index 6040255268..baa44ca05f 100644 --- a/acceptance/openstack/networking/v2/extensions/qos/ruletypes/ruletypes_test.go +++ b/acceptance/openstack/networking/v2/extensions/qos/ruletypes/ruletypes_test.go @@ -5,20 +5,23 @@ import ( "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/common/extensions" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/qos/ruletypes" ) func TestRuleTypes(t *testing.T) { - clients.SkipRelease(t, "stable/mitaka") - clients.SkipRelease(t, "stable/newton") - clients.SkipRelease(t, "stable/ocata") - client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) return } + extension, err := extensions.Get(client, "qos").Extract() + if err != nil { + t.Skip("This test requires qos Neutron extension") + } + tools.PrintResource(t, extension) + page, err := ruletypes.ListRuleTypes(client).AllPages() if err != nil { t.Fatalf("Failed to list rule types pages: %v", err) diff --git a/acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go b/acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go index 50b02d7b30..0e62059278 100644 --- a/acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go +++ b/acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go @@ -10,20 +10,24 @@ import ( "github.com/gophercloud/gophercloud/acceptance/clients" v2 "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2" "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/common/extensions" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/attributestags" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/trunks" th "github.com/gophercloud/gophercloud/testhelper" ) func TestTrunkCRUD(t *testing.T) { - clients.SkipRelease(t, "stable/mitaka") - t.Skip("Currently failing in OpenLab") - client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) } + extension, err := extensions.Get(client, "trunk").Extract() + if err != nil { + t.Skip("This test requires trunk Neutron extension") + } + tools.PrintResource(t, extension) + // Create Network network, err := v2.CreateNetwork(t, client) if err != nil { @@ -103,14 +107,17 @@ func TestTrunkCRUD(t *testing.T) { } func TestTrunkList(t *testing.T) { - clients.SkipRelease(t, "stable/mitaka") - t.Skip("Currently failing in OpenLab") - client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) } + extension, err := extensions.Get(client, "trunk").Extract() + if err != nil { + t.Skip("This test requires trunk Neutron extension") + } + tools.PrintResource(t, extension) + allPages, err := trunks.List(client, nil).AllPages() if err != nil { t.Fatalf("Unable to list trunks: %v", err) @@ -127,14 +134,17 @@ func TestTrunkList(t *testing.T) { } func TestTrunkSubportOperation(t *testing.T) { - clients.SkipRelease(t, "stable/mitaka") - t.Skip("Currently failing in OpenLab") - client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) } + extension, err := extensions.Get(client, "trunk").Extract() + if err != nil { + t.Skip("This test requires trunk Neutron extension") + } + tools.PrintResource(t, extension) + // Create Network network, err := v2.CreateNetwork(t, client) if err != nil { @@ -212,16 +222,17 @@ func TestTrunkSubportOperation(t *testing.T) { } func TestTrunkTags(t *testing.T) { - clients.SkipRelease(t, "stable/mitaka") - clients.SkipRelease(t, "stable/newton") - clients.SkipRelease(t, "stable/ocata") - t.Skip("Currently failing in OpenLab") - client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) } + extension, err := extensions.Get(client, "trunk").Extract() + if err != nil { + t.Skip("This test requires trunk Neutron extension") + } + tools.PrintResource(t, extension) + // Create Network network, err := v2.CreateNetwork(t, client) if err != nil { diff --git a/acceptance/openstack/networking/v2/extensions/vlantransparent/vlantransparent_test.go b/acceptance/openstack/networking/v2/extensions/vlantransparent/vlantransparent_test.go index fc6df0625c..ff5692752a 100644 --- a/acceptance/openstack/networking/v2/extensions/vlantransparent/vlantransparent_test.go +++ b/acceptance/openstack/networking/v2/extensions/vlantransparent/vlantransparent_test.go @@ -9,15 +9,20 @@ import ( "github.com/gophercloud/gophercloud/acceptance/clients" networkingv2 "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2" "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/common/extensions" th "github.com/gophercloud/gophercloud/testhelper" ) func TestVLANTransparentCRUD(t *testing.T) { - t.Skip("We don't have VLAN transparent extension in OpenLab.") - client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) + extension, err := extensions.Get(client, "vlan-transparent").Extract() + if err != nil { + t.Skip("This test requires vlan-transparent Neutron extension") + } + tools.PrintResource(t, extension) + // Create a VLAN transparent network. network, err := CreateVLANTransparentNetwork(t, client) th.AssertNoErr(t, err) diff --git a/acceptance/openstack/networking/v2/extensions/vpnaas/service_test.go b/acceptance/openstack/networking/v2/extensions/vpnaas/service_test.go index 5c62403e23..d8b7daa42b 100644 --- a/acceptance/openstack/networking/v2/extensions/vpnaas/service_test.go +++ b/acceptance/openstack/networking/v2/extensions/vpnaas/service_test.go @@ -29,6 +29,7 @@ func TestServiceList(t *testing.T) { } func TestServiceCRUD(t *testing.T) { + clients.SkipReleasesAbove(t, "stable/wallaby") client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) diff --git a/acceptance/openstack/networking/v2/extensions/vpnaas/siteconnection_test.go b/acceptance/openstack/networking/v2/extensions/vpnaas/siteconnection_test.go index b9ce51b099..5f8bf4bea0 100644 --- a/acceptance/openstack/networking/v2/extensions/vpnaas/siteconnection_test.go +++ b/acceptance/openstack/networking/v2/extensions/vpnaas/siteconnection_test.go @@ -31,6 +31,7 @@ func TestConnectionList(t *testing.T) { } func TestConnectionCRUD(t *testing.T) { + clients.SkipReleasesAbove(t, "stable/wallaby") client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) From b614a862777ca57b38b68240b41cceb3ed206643 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= <m.andre@redhat.com> Date: Wed, 26 Jan 2022 09:25:31 +0100 Subject: [PATCH 043/360] Fix triggers for basic and dns functional workflows It's not using regex but instead bash style globbing [1]. Follow-up to #2334. [1] https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#patterns-to-match-file-paths --- .github/workflows/functional-basic.yaml | 5 ++++- .github/workflows/functional-dns.yaml | 4 ++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/functional-basic.yaml b/.github/workflows/functional-basic.yaml index dadd3bdc89..4ae4292793 100644 --- a/.github/workflows/functional-basic.yaml +++ b/.github/workflows/functional-basic.yaml @@ -2,7 +2,10 @@ name: functional-basic on: pull_request: paths-ignore: - - '^docs/|\.md$|^(?:.*/)?(?:\.gitignore|LICENSE)$' + - 'docs/**' + - '**.md' + - '**.gitignore' + - '**LICENSE' schedule: - cron: '0 0 * * *' jobs: diff --git a/.github/workflows/functional-dns.yaml b/.github/workflows/functional-dns.yaml index 8bb0d4e10e..a72fbda03d 100644 --- a/.github/workflows/functional-dns.yaml +++ b/.github/workflows/functional-dns.yaml @@ -2,8 +2,8 @@ name: functional-dns on: pull_request: paths: - - '^acceptance/openstack/dns.*$' - - '.github/workflows/functional-dns.yaml' + - 'acceptance/openstack/dns**' + - '**functional-dns.yaml' schedule: - cron: '0 0 * * *' jobs: From 72f68fd89b1334f67fba06ec864876403eac542d Mon Sep 17 00:00:00 2001 From: Emilien Macchi <emilien@redhat.com> Date: Fri, 28 Jan 2022 08:23:52 -0500 Subject: [PATCH 044/360] [CI-v2] Add objectstorage job with Github Action Test Swift with a github action & devstack. --- .../workflows/functional-objectstorage.yaml | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 .github/workflows/functional-objectstorage.yaml diff --git a/.github/workflows/functional-objectstorage.yaml b/.github/workflows/functional-objectstorage.yaml new file mode 100644 index 0000000000..6e1f365efb --- /dev/null +++ b/.github/workflows/functional-objectstorage.yaml @@ -0,0 +1,62 @@ +name: functional-objectstorage +on: + pull_request: + paths: + - '**objectstorage**' + schedule: + - cron: '0 0 * * *' +jobs: + functional-objectstorage: + strategy: + fail-fast: false + matrix: + name: ["master"] + openstack_version: ["master"] + ubuntu_version: ["20.04"] + include: + - name: "xena" + openstack_version: "stable/xena" + ubuntu_version: "20.04" + - name: "wallaby" + openstack_version: "stable/wallaby" + ubuntu_version: "20.04" + - name: "victoria" + openstack_version: "stable/victoria" + ubuntu_version: "20.04" + - name: "ussuri" + openstack_version: "stable/ussuri" + ubuntu_version: "18.04" + - name: "train" + openstack_version: "stable/train" + ubuntu_version: "18.04" + runs-on: ubuntu-${{ matrix.ubuntu_version }} + name: Deploy OpenStack ${{ matrix.name }} with Swift and run objectstorage acceptance tests + steps: + - name: Checkout Gophercloud + uses: actions/checkout@v2 + - name: Deploy devstack + uses: EmilienM/devstack-action@v0.6 + with: + branch: ${{ matrix.openstack_version }} + conf_overrides: | + SWIFT_ENABLE_TEMPURLS=True + SWIFT_TEMPURL_KEY=secretkey + enabled_services: 's-account,s-container,s-object,s-proxy' + - name: Checkout go + uses: actions/setup-go@v2 + with: + go-version: '^1.15' + - name: Run Gophercloud acceptance tests + run: ./script/acceptancetest + env: + DEVSTACK_PATH: ${{ github.workspace }}/devstack + ACCEPTANCE_TESTS_FILTER: '^.*objectstorage.*$' + - name: Generate logs on failure + run: ./script/collectlogs + if: failure() + - name: Upload logs artifacts on failure + if: failure() + uses: actions/upload-artifact@v2 + with: + name: functional-objectstorage-${{ matrix.name }} + path: /tmp/devstack-logs/* From e3b9aad25ce5797c9ed53c141fecac8c509c9f34 Mon Sep 17 00:00:00 2001 From: Emilien Macchi <emilien@redhat.com> Date: Fri, 28 Jan 2022 08:47:32 -0500 Subject: [PATCH 045/360] conditions: use IsReleases* from SkipReleases* Re-use a function instead of duplicating the conditions. --- acceptance/clients/conditions.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/acceptance/clients/conditions.go b/acceptance/clients/conditions.go index 1a14b8898f..e237143be9 100644 --- a/acceptance/clients/conditions.go +++ b/acceptance/clients/conditions.go @@ -87,7 +87,7 @@ func SkipRelease(t *testing.T, release string) { func SkipReleasesBelow(t *testing.T, release string) { current_branch := os.Getenv("OS_BRANCH") - if current_branch != "master" && current_branch < release { + if IsReleasesBelow(t, release) { t.Skipf("this is not supported below %s, testing in %s", release, current_branch) } } @@ -99,7 +99,7 @@ func SkipReleasesAbove(t *testing.T, release string) { current_branch := os.Getenv("OS_BRANCH") // Assume master is always too new - if current_branch == "master" || current_branch > release { + if IsReleasesAbove(t, release) { t.Skipf("this is not supported above %s, testing in %s", release, current_branch) } } From 613261b5b52e9b41c53e5c421a972b0f9416e61a Mon Sep 17 00:00:00 2001 From: freedywu <wufreedy@gmail.com> Date: Mon, 10 Jan 2022 16:40:34 +0800 Subject: [PATCH 046/360] Identity v2&v3: add RequestOpts.OmitHeaders, remove blank header --- openstack/identity/v2/tokens/requests.go | 2 +- openstack/identity/v3/tokens/requests.go | 2 +- provider_client.go | 15 +++++++++++---- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/openstack/identity/v2/tokens/requests.go b/openstack/identity/v2/tokens/requests.go index 2b64f108cb..84f16c3fc2 100644 --- a/openstack/identity/v2/tokens/requests.go +++ b/openstack/identity/v2/tokens/requests.go @@ -89,7 +89,7 @@ func Create(client *gophercloud.ServiceClient, auth AuthOptionsBuilder) (r Creat } resp, err := client.Post(CreateURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 203}, - MoreHeaders: map[string]string{"X-Auth-Token": ""}, + OmitHeaders: []string{"X-Auth-Token"}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return diff --git a/openstack/identity/v3/tokens/requests.go b/openstack/identity/v3/tokens/requests.go index d8c455d160..1af55d8137 100644 --- a/openstack/identity/v3/tokens/requests.go +++ b/openstack/identity/v3/tokens/requests.go @@ -135,7 +135,7 @@ func Create(c *gophercloud.ServiceClient, opts AuthOptionsBuilder) (r CreateResu } resp, err := c.Post(tokenURL(c), b, &r.Body, &gophercloud.RequestOpts{ - MoreHeaders: map[string]string{"X-Auth-Token": ""}, + OmitHeaders: []string{"X-Auth-Token"}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return diff --git a/provider_client.go b/provider_client.go index 207ab88af8..b4ee943dcd 100644 --- a/provider_client.go +++ b/provider_client.go @@ -325,10 +325,12 @@ type RequestOpts struct { // 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 provide on the request. If a header is - // provided with a blank value (""), that header will be *omitted* instead: use this to suppress - // the default Accept header or an inferred Content-Type, for example. + // 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 // ErrorContext specifies the resource error type to return if an error is encountered. // This lets resources override default error messages based on the response status code. ErrorContext error @@ -396,7 +398,8 @@ func (client *ProviderClient) doRequest(method, url string, options *RequestOpts req = req.WithContext(client.Context) } - // Populate the request headers. Apply options.MoreHeaders last, to give the caller the chance to + // Populate the request headers. + // Apply options.MoreHeaders and options.OmitHeaders, to give the caller the chance to // modify or omit any header. if contentType != nil { req.Header.Set("Content-Type", *contentType) @@ -412,6 +415,10 @@ func (client *ProviderClient) doRequest(method, url string, options *RequestOpts } } + 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) From 1e8f0fd21e48e9f42d7f485011598d7dd4f22c65 Mon Sep 17 00:00:00 2001 From: Ching Kuo <igene@igene.tw> Date: Sat, 18 Dec 2021 23:30:04 +0800 Subject: [PATCH 047/360] Add SOURCE_IP_PORT as lb_algorithm This commit added SOURCE_IP_PORT to lb_algorithm options. It is the only load balancer algorithm supported by OVN. --- openstack/loadbalancer/v2/pools/requests.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/openstack/loadbalancer/v2/pools/requests.go b/openstack/loadbalancer/v2/pools/requests.go index 087e95cab2..41d7dd9a41 100644 --- a/openstack/loadbalancer/v2/pools/requests.go +++ b/openstack/loadbalancer/v2/pools/requests.go @@ -65,6 +65,7 @@ const ( LBMethodRoundRobin LBMethod = "ROUND_ROBIN" LBMethodLeastConnections LBMethod = "LEAST_CONNECTIONS" LBMethodSourceIp LBMethod = "SOURCE_IP" + LBMethodSourceIpPort LBMethod = "SOURCE_IP_PORT" ProtocolTCP Protocol = "TCP" ProtocolUDP Protocol = "UDP" @@ -87,8 +88,8 @@ type CreateOptsBuilder interface { // operation. type CreateOpts struct { // The algorithm used to distribute load between the members of the pool. The - // current specification supports LBMethodRoundRobin, LBMethodLeastConnections - // and LBMethodSourceIp as valid values for this attribute. + // current specification supports LBMethodRoundRobin, LBMethodLeastConnections, + // LBMethodSourceIp and LBMethodSourceIpPort as valid values for this attribute. LBMethod LBMethod `json:"lb_algorithm" required:"true"` // The protocol used by the pool members, you can use either @@ -181,8 +182,8 @@ type UpdateOpts struct { Description *string `json:"description,omitempty"` // The algorithm used to distribute load between the members of the pool. The - // current specification supports LBMethodRoundRobin, LBMethodLeastConnections - // and LBMethodSourceIp as valid values for this attribute. + // current specification supports LBMethodRoundRobin, LBMethodLeastConnections, + // LBMethodSourceIp and LBMethodSourceIpPort as valid values for this attribute. LBMethod LBMethod `json:"lb_algorithm,omitempty"` // The administrative state of the Pool. A valid value is true (UP) From d30b0621d4feb64f249e19948104618e506d8987 Mon Sep 17 00:00:00 2001 From: douyali <douyali@troila.com> Date: Tue, 8 Feb 2022 10:54:17 +0800 Subject: [PATCH 048/360] blockstorage/noauth: Add Client Type Support --- acceptance/clients/clients.go | 4 ++-- openstack/blockstorage/noauth/doc.go | 2 +- openstack/blockstorage/noauth/requests.go | 15 ++++++++++----- .../blockstorage/noauth/testing/requests_test.go | 6 +++--- 4 files changed, 16 insertions(+), 11 deletions(-) diff --git a/acceptance/clients/clients.go b/acceptance/clients/clients.go index eecec73903..3770f0060f 100644 --- a/acceptance/clients/clients.go +++ b/acceptance/clients/clients.go @@ -211,7 +211,7 @@ func NewBlockStorageV2NoAuthClient() (*gophercloud.ServiceClient, error) { client = configureDebug(client) - return blockstorageNoAuth.NewBlockStorageNoAuth(client, blockstorageNoAuth.EndpointOpts{ + return blockstorageNoAuth.NewBlockStorageNoAuthV2(client, blockstorageNoAuth.EndpointOpts{ CinderEndpoint: os.Getenv("CINDER_ENDPOINT"), }) } @@ -230,7 +230,7 @@ func NewBlockStorageV3NoAuthClient() (*gophercloud.ServiceClient, error) { client = configureDebug(client) - return blockstorageNoAuth.NewBlockStorageNoAuth(client, blockstorageNoAuth.EndpointOpts{ + return blockstorageNoAuth.NewBlockStorageNoAuthV3(client, blockstorageNoAuth.EndpointOpts{ CinderEndpoint: os.Getenv("CINDER_ENDPOINT"), }) } diff --git a/openstack/blockstorage/noauth/doc.go b/openstack/blockstorage/noauth/doc.go index 25a7f84582..3ecc366a3b 100644 --- a/openstack/blockstorage/noauth/doc.go +++ b/openstack/blockstorage/noauth/doc.go @@ -8,7 +8,7 @@ Example of Creating a noauth Service Client Username: os.Getenv("OS_USERNAME"), TenantName: os.Getenv("OS_TENANT_NAME"), }) - client, err := noauth.NewBlockStorageNoAuth(provider, noauth.EndpointOpts{ + client, err := noauth.NewBlockStorageNoAuthV2(provider, noauth.EndpointOpts{ CinderEndpoint: os.Getenv("CINDER_ENDPOINT"), }) diff --git a/openstack/blockstorage/noauth/requests.go b/openstack/blockstorage/noauth/requests.go index 21cc8f09df..147fd8e609 100644 --- a/openstack/blockstorage/noauth/requests.go +++ b/openstack/blockstorage/noauth/requests.go @@ -31,7 +31,7 @@ func NewClient(options gophercloud.AuthOptions) (*gophercloud.ProviderClient, er return client, nil } -func initClientOpts(client *gophercloud.ProviderClient, eo EndpointOpts) (*gophercloud.ServiceClient, error) { +func initClientOpts(client *gophercloud.ProviderClient, eo EndpointOpts, clientType string) (*gophercloud.ServiceClient, error) { sc := new(gophercloud.ServiceClient) if eo.CinderEndpoint == "" { return nil, fmt.Errorf("CinderEndpoint is required") @@ -45,11 +45,16 @@ func initClientOpts(client *gophercloud.ProviderClient, eo EndpointOpts) (*gophe endpoint := fmt.Sprintf("%s%s", gophercloud.NormalizeURL(eo.CinderEndpoint), token[1]) sc.Endpoint = gophercloud.NormalizeURL(endpoint) sc.ProviderClient = client + sc.Type = clientType return sc, nil } -// NewBlockStorageNoAuth creates a ServiceClient that may be used to access a -// "noauth" block storage service (V2 or V3 Cinder API). -func NewBlockStorageNoAuth(client *gophercloud.ProviderClient, eo EndpointOpts) (*gophercloud.ServiceClient, error) { - return initClientOpts(client, eo) +// NewBlockStorageNoAuthV2 creates a ServiceClient that may be used to access "noauth" v2 block storage service. +func NewBlockStorageNoAuthV2(client *gophercloud.ProviderClient, eo EndpointOpts) (*gophercloud.ServiceClient, error) { + return initClientOpts(client, eo, "volumev2") +} + +// NewBlockStorageNoAuthV3 creates a ServiceClient that may be used to access "noauth" v3 block storage service. +func NewBlockStorageNoAuthV3(client *gophercloud.ProviderClient, eo EndpointOpts) (*gophercloud.ServiceClient, error) { + return initClientOpts(client, eo, "volumev3") } diff --git a/openstack/blockstorage/noauth/testing/requests_test.go b/openstack/blockstorage/noauth/testing/requests_test.go index 14080259aa..72f933a083 100644 --- a/openstack/blockstorage/noauth/testing/requests_test.go +++ b/openstack/blockstorage/noauth/testing/requests_test.go @@ -15,7 +15,7 @@ func TestNoAuth(t *testing.T) { } provider, err := noauth.NewClient(ao) th.AssertNoErr(t, err) - noauthClient, err := noauth.NewBlockStorageNoAuth(provider, noauth.EndpointOpts{ + noauthClient, err := noauth.NewBlockStorageNoAuthV2(provider, noauth.EndpointOpts{ CinderEndpoint: "http://cinder:8776/v2", }) th.AssertNoErr(t, err) @@ -25,14 +25,14 @@ func TestNoAuth(t *testing.T) { ao2 := gophercloud.AuthOptions{} provider2, err := noauth.NewClient(ao2) th.AssertNoErr(t, err) - noauthClient2, err := noauth.NewBlockStorageNoAuth(provider2, noauth.EndpointOpts{ + noauthClient2, err := noauth.NewBlockStorageNoAuthV2(provider2, noauth.EndpointOpts{ CinderEndpoint: "http://cinder:8776/v2/", }) th.AssertNoErr(t, err) th.AssertEquals(t, naResult.Endpoint, noauthClient2.Endpoint) th.AssertEquals(t, naResult.TokenID, noauthClient2.TokenID) - errTest, err := noauth.NewBlockStorageNoAuth(provider2, noauth.EndpointOpts{}) + errTest, err := noauth.NewBlockStorageNoAuthV2(provider2, noauth.EndpointOpts{}) _ = errTest th.AssertEquals(t, errorResult, err.Error()) } From 659c8a2afd72f1f37947971be0d7994d53daa5f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sandro=20J=C3=A4ckel?= <sandro.jaeckel@sap.com> Date: Wed, 9 Feb 2022 12:28:58 +0100 Subject: [PATCH 049/360] Fix typo --- openstack/blockstorage/extensions/schedulerstats/results.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openstack/blockstorage/extensions/schedulerstats/results.go b/openstack/blockstorage/extensions/schedulerstats/results.go index fe5ea2b4fc..1be3f4778a 100644 --- a/openstack/blockstorage/extensions/schedulerstats/results.go +++ b/openstack/blockstorage/extensions/schedulerstats/results.go @@ -29,7 +29,7 @@ type Capabilities struct { ThickProvisioningSupport bool `json:"thick_provisioning_support"` TotalVolumes int64 `json:"total_volumes"` FilterFunction string `json:"filter_function"` - GoodnessFuction string `json:"goodness_function"` + GoodnessFunction string `json:"goodness_function"` Multiattach bool `json:"multiattach"` SparseCopyVolume bool `json:"sparse_copy_volume"` } From 023b1d7a82dff2efe86ab38173fd064d98810367 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sandro=20J=C3=A4ckel?= <sandro.jaeckel@sap.com> Date: Wed, 9 Feb 2022 13:33:57 +0100 Subject: [PATCH 050/360] Add allocated_capacity_gb to blockstorage/extensions/schedulerstats --- openstack/blockstorage/extensions/schedulerstats/results.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/openstack/blockstorage/extensions/schedulerstats/results.go b/openstack/blockstorage/extensions/schedulerstats/results.go index fe5ea2b4fc..7e20fb2eac 100644 --- a/openstack/blockstorage/extensions/schedulerstats/results.go +++ b/openstack/blockstorage/extensions/schedulerstats/results.go @@ -32,6 +32,7 @@ type Capabilities struct { GoodnessFuction string `json:"goodness_function"` Multiattach bool `json:"multiattach"` SparseCopyVolume bool `json:"sparse_copy_volume"` + AllocatedCapacityGB float64 `json:"-"` } // StoragePool represents an individual StoragePool retrieved from the @@ -45,6 +46,7 @@ func (r *Capabilities) UnmarshalJSON(b []byte) error { type tmp Capabilities var s struct { tmp + AllocatedCapacityGB interface{} `json:"allocated_capacity_gb"` FreeCapacityGB interface{} `json:"free_capacity_gb"` MaxOverSubscriptionRatio interface{} `json:"max_over_subscription_ratio"` TotalCapacityGB interface{} `json:"total_capacity_gb"` @@ -71,6 +73,7 @@ func (r *Capabilities) UnmarshalJSON(b []byte) error { return 0.0 } + r.AllocatedCapacityGB = parseCapacity(s.AllocatedCapacityGB) r.FreeCapacityGB = parseCapacity(s.FreeCapacityGB) r.TotalCapacityGB = parseCapacity(s.TotalCapacityGB) From 166e20329f834b31c5616cfc0cf2eab265ee3d1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sandro=20J=C3=A4ckel?= <sandro.jaeckel@sap.com> Date: Tue, 15 Feb 2022 14:59:25 +0100 Subject: [PATCH 051/360] dns/v2/recordsets: add missing metadata field --- openstack/dns/v2/recordsets/results.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/openstack/dns/v2/recordsets/results.go b/openstack/dns/v2/recordsets/results.go index 0fdc1fe52e..18a0cbd59a 100644 --- a/openstack/dns/v2/recordsets/results.go +++ b/openstack/dns/v2/recordsets/results.go @@ -112,6 +112,11 @@ type RecordSet struct { // useful for passing along to other APIs that might want a recordset // reference. Links []gophercloud.Link `json:"-"` + + // Metadata contains the total_count of resources matching the filter + Metadata struct { + TotalCount int `json:"total_count"` + } `json:"metadata"` } func (r *RecordSet) UnmarshalJSON(b []byte) error { From 0fbcbfec394fc063661ff67e7c83dc7fc15a98a3 Mon Sep 17 00:00:00 2001 From: Emilien Macchi <emilien@redhat.com> Date: Tue, 9 Nov 2021 21:12:21 -0500 Subject: [PATCH 052/360] Add new workflow for greetings Add a new workflow to be welcome our new contributors and let them know how to find help. --- .github/workflows/greetings.yml | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 .github/workflows/greetings.yml diff --git a/.github/workflows/greetings.yml b/.github/workflows/greetings.yml new file mode 100644 index 0000000000..4203b030b9 --- /dev/null +++ b/.github/workflows/greetings.yml @@ -0,0 +1,24 @@ +name: Greetings + +on: [pull_request, issues] + +jobs: + greeting: + runs-on: ubuntu-latest + permissions: + issues: write + pull-requests: write + steps: + - uses: actions/first-interaction@v1 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + issue-message: | + Thank you for reporting your first issue! Be sure that we will be looking at it, but keep in mind + this sometimes takes a while. + Please let the maintainers know if your issue has not got enough attention after a few days. + If any doubt, please consult our issue [tutorial](https://github.com/gophercloud/gophercloud/blob/master/docs/contributor-tutorial/step-02-issues.md). + pr-message: | + Thank you for submitting your first PR! Be sure that we will be looking at it but keep in mind + this sometimes takes a while. + Please let the maintainers know if your PR has not got enough attention after a few days. + If any doubt, please consult our PR [tutorial](https://github.com/gophercloud/gophercloud/blob/master/docs/contributor-tutorial/step-05-pull-requests.md). From ef7a386512536857c06f4dba4403589959776631 Mon Sep 17 00:00:00 2001 From: Emilien Macchi <emilien@redhat.com> Date: Mon, 14 Feb 2022 11:27:41 -0500 Subject: [PATCH 053/360] zuul: remove network job we replaced it by github actions, coverage is better now and more stable. --- .zuul.yaml | 164 +---------------------------------------------------- 1 file changed, 1 insertion(+), 163 deletions(-) diff --git a/.zuul.yaml b/.zuul.yaml index ab32778978..8bf374cf04 100644 --- a/.zuul.yaml +++ b/.zuul.yaml @@ -42,47 +42,6 @@ - heat - zun -- job: - name: gophercloud-acceptance-test-networking - parent: gophercloud-acceptance-test-base - description: | - Run gophercloud networking acceptance test on master branch - files: - - ^.*dns.*$ - - ^.*loadbalancer.*$ - - ^.*networking.*$ - vars: - prefetch_amphora: true - devstack_env: - OCTAVIA_AMP_IMAGE_FILE: /opt/octavia-amphora/amphora-x64-haproxy.qcow2 - # the list of all available tests can generated by: - # find acceptance/openstack -name '*_test.go' -exec dirname {} \; | sort -n | uniq -c | awk '{print $2}' - acceptance_tests: - - acceptance/openstack/dns/v2 - - acceptance/openstack/loadbalancer/v2 - - acceptance/openstack/networking/v2 - - acceptance/openstack/networking/v2/extensions - - acceptance/openstack/networking/v2/extensions/agents - - acceptance/openstack/networking/v2/extensions/dns - - acceptance/openstack/networking/v2/extensions/layer3 - - acceptance/openstack/networking/v2/extensions/mtu - - acceptance/openstack/networking/v2/extensions/networkipavailabilities - - acceptance/openstack/networking/v2/extensions/portsbinding - - acceptance/openstack/networking/v2/extensions/qos/policies - - acceptance/openstack/networking/v2/extensions/qos/rules - - acceptance/openstack/networking/v2/extensions/qos/ruletypes - - acceptance/openstack/networking/v2/extensions/quotas - - acceptance/openstack/networking/v2/extensions/rbacpolicies - - acceptance/openstack/networking/v2/extensions/subnetpools - - acceptance/openstack/networking/v2/extensions/trunks - - acceptance/openstack/networking/v2/extensions/vlantransparent - devstack_projects: 'openstack/neutron-dynamic-routing' - devstack_services: - - designate - - neutron-dynamic-routing - - neutron-ext - - octavia - - job: name: gophercloud-acceptance-test-storage parent: gophercloud-acceptance-test-base @@ -115,16 +74,6 @@ global_env: OS_BRANCH: stable/ussuri -- job: - name: gophercloud-acceptance-test-networking-ussuri - parent: gophercloud-acceptance-test-networking - nodeset: ubuntu-bionic - description: | - Run gophercloud networking acceptance test on ussuri branch - vars: - global_env: - OS_BRANCH: stable/ussuri - - job: name: gophercloud-acceptance-test-storage-ussuri parent: gophercloud-acceptance-test-storage @@ -145,16 +94,6 @@ global_env: OS_BRANCH: stable/train -- job: - name: gophercloud-acceptance-test-networking-train - parent: gophercloud-acceptance-test-networking - nodeset: ubuntu-xenial - description: | - Run gophercloud networking acceptance test on train branch - vars: - global_env: - OS_BRANCH: stable/train - - job: name: gophercloud-acceptance-test-storage-train parent: gophercloud-acceptance-test-storage @@ -175,16 +114,6 @@ global_env: OS_BRANCH: stable/stein -- job: - name: gophercloud-acceptance-test-networking-stein - parent: gophercloud-acceptance-test-networking - nodeset: ubuntu-xenial - description: | - Run gophercloud networking acceptance test on stein branch - vars: - global_env: - OS_BRANCH: stable/stein - - job: name: gophercloud-acceptance-test-storage-stein parent: gophercloud-acceptance-test-storage @@ -205,16 +134,6 @@ global_env: OS_BRANCH: stable/rocky -- job: - name: gophercloud-acceptance-test-networking-rocky - parent: gophercloud-acceptance-test-networking - nodeset: ubuntu-xenial - description: | - Run gophercloud networking acceptance test on rocky branch - vars: - global_env: - OS_BRANCH: stable/rocky - - job: name: gophercloud-acceptance-test-storage-rocky parent: gophercloud-acceptance-test-storage @@ -235,16 +154,6 @@ global_env: OS_BRANCH: stable/queens -- job: - name: gophercloud-acceptance-test-networking-queens - parent: gophercloud-acceptance-test-networking - description: | - Run gophercloud networking acceptance test on queens branch - nodeset: ubuntu-xenial - vars: - global_env: - OS_BRANCH: stable/queens - - job: name: gophercloud-acceptance-test-storage-queens parent: gophercloud-acceptance-test-storage @@ -267,16 +176,6 @@ global_env: OS_BRANCH: stable/pike -- job: - name: gophercloud-acceptance-test-networking-pike - parent: gophercloud-acceptance-test-networking - description: | - Run gophercloud networking acceptance test on pike branch - nodeset: ubuntu-xenial - vars: - global_env: - OS_BRANCH: stable/pike - - job: name: gophercloud-acceptance-test-storage-pike parent: gophercloud-acceptance-test-storage @@ -297,16 +196,6 @@ global_env: OS_BRANCH: stable/ocata -- job: - name: gophercloud-acceptance-test-networking-ocata - parent: gophercloud-acceptance-test-networking - description: | - Run gophercloud networking acceptance test on ocata branch - nodeset: ubuntu-xenial - vars: - global_env: - OS_BRANCH: stable/ocata - - job: name: gophercloud-acceptance-test-storage-ocata parent: gophercloud-acceptance-test-storage @@ -329,16 +218,6 @@ global_env: OS_BRANCH: stable/newton -- job: - name: gophercloud-acceptance-test-networking-newton - parent: gophercloud-acceptance-test-networking - description: | - Run gophercloud networking acceptance test on newton branch - nodeset: ubuntu-xenial - vars: - global_env: - OS_BRANCH: stable/newton - - job: name: gophercloud-acceptance-test-storage-newton parent: gophercloud-acceptance-test-storage @@ -351,7 +230,7 @@ # The following jobs are maintained because they are parents # for gophercloud v0.4.0 acceptance tests in theopenlab/openlab-zuul-jobs. -# TODO(emilien) update the childs to use the new variants. +# It'll be removed once we finished the migration to Github actions. - job: name: gophercloud-acceptance-test-legacy parent: gophercloud-acceptance-test-base @@ -360,47 +239,15 @@ Run gophercloud acceptance test on the master branch. This runs when any file is patched except if it's doc. vars: - # the list of all available tests can generated by: - # find acceptance/openstack -name '*_test.go' -exec dirname {} \; | sort -n | uniq -c | awk '{print $2}' acceptance_tests: - acceptance/openstack - - acceptance/openstack/blockstorage - - acceptance/openstack/blockstorage/extensions - - acceptance/openstack/blockstorage/v3 - - acceptance/openstack/compute/v2 - acceptance/openstack/container/v1 - - acceptance/openstack/dns/v2 - - acceptance/openstack/identity/v2 - - acceptance/openstack/identity/v3 - - acceptance/openstack/imageservice/v2 - - acceptance/openstack/keymanager/v1 - - acceptance/openstack/loadbalancer/v2 - - acceptance/openstack/networking/v2 - - acceptance/openstack/networking/v2/extensions - - acceptance/openstack/networking/v2/extensions/agents - - acceptance/openstack/networking/v2/extensions/dns - - acceptance/openstack/networking/v2/extensions/layer3 - - acceptance/openstack/networking/v2/extensions/mtu - - acceptance/openstack/networking/v2/extensions/networkipavailabilities - - acceptance/openstack/networking/v2/extensions/portsbinding - - acceptance/openstack/networking/v2/extensions/qos/policies - - acceptance/openstack/networking/v2/extensions/qos/rules - - acceptance/openstack/networking/v2/extensions/qos/ruletypes - - acceptance/openstack/networking/v2/extensions/quotas - - acceptance/openstack/networking/v2/extensions/rbacpolicies - - acceptance/openstack/networking/v2/extensions/subnetpools - - acceptance/openstack/networking/v2/extensions/trunks - - acceptance/openstack/networking/v2/extensions/vlantransparent - - acceptance/openstack/objectstorage/v1 - acceptance/openstack/orchestration/v1 - acceptance/openstack/placement/v1 - acceptance/openstack/sharedfilesystems/v2 - acceptance/openstack/sharedfilesystems/v2/messages devstack_services: - - designate - manila - - neutron-ext - - octavia - zun - job: @@ -473,45 +320,36 @@ jobs: - gophercloud-unittest - gophercloud-acceptance-test-compute - - gophercloud-acceptance-test-networking - gophercloud-acceptance-test-storage recheck-newton: jobs: - gophercloud-acceptance-test-compute-newton - - gophercloud-acceptance-test-networking-newton - gophercloud-acceptance-test-storage-newton recheck-ocata: jobs: - gophercloud-acceptance-test-compute-ocata - - gophercloud-acceptance-test-networking-ocata - gophercloud-acceptance-test-storage-ocata recheck-pike: jobs: - gophercloud-acceptance-test-compute-pike - - gophercloud-acceptance-test-networking-pike - gophercloud-acceptance-test-storage-pike recheck-queens: jobs: - gophercloud-acceptance-test-compute-queens - - gophercloud-acceptance-test-networking-queens - gophercloud-acceptance-test-storage-queens recheck-rocky: jobs: - gophercloud-acceptance-test-compute-rocky - - gophercloud-acceptance-test-networking-rocky - gophercloud-acceptance-test-storage-rocky recheck-stein: jobs: - gophercloud-acceptance-test-compute-stein - - gophercloud-acceptance-test-networking-stein - gophercloud-acceptance-test-storage-stein recheck-train: jobs: - gophercloud-acceptance-test-compute-train - - gophercloud-acceptance-test-networking-train - gophercloud-acceptance-test-storage-train recheck-ussuri: jobs: - gophercloud-acceptance-test-compute-ussuri - - gophercloud-acceptance-test-networking-ussuri - gophercloud-acceptance-test-storage-ussuri From c7c54dccb60fadd524cdfe5aef2e185ed0febcd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sandro=20J=C3=A4ckel?= <sandro.jaeckel@sap.com> Date: Tue, 15 Feb 2022 16:24:43 +0100 Subject: [PATCH 054/360] compute/v2/extensions/servergroups: add optional limit and offset to ListOpts --- openstack/compute/v2/extensions/servergroups/requests.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/openstack/compute/v2/extensions/servergroups/requests.go b/openstack/compute/v2/extensions/servergroups/requests.go index 8e7966d567..9769e48deb 100644 --- a/openstack/compute/v2/extensions/servergroups/requests.go +++ b/openstack/compute/v2/extensions/servergroups/requests.go @@ -12,6 +12,12 @@ type ListOptsBuilder interface { type ListOpts struct { // AllProjects is a bool to show all projects. AllProjects bool `q:"all_projects"` + + // 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"` } // ToServerListQuery formats a ListOpts into a query string. From abb1cf1e3229369fca4293917ae2490e5540d8d9 Mon Sep 17 00:00:00 2001 From: nikParasyr <nik.parasyr@protonmail.com> Date: Thu, 24 Feb 2022 19:32:37 +0100 Subject: [PATCH 055/360] Add List for identity v3 limits [Docs](https://docs.openstack.org/api-ref/identity/v3/?expanded=list-limits-detail#unified-limits) --- .../openstack/identity/v3/limits_test.go | 27 ++++++ openstack/identity/v3/limits/doc.go | 22 +++++ openstack/identity/v3/limits/requests.go | 51 ++++++++++ openstack/identity/v3/limits/results.go | 71 ++++++++++++++ .../identity/v3/limits/testing/fixtures.go | 93 +++++++++++++++++++ .../v3/limits/testing/requests_test.go | 42 +++++++++ openstack/identity/v3/limits/urls.go | 7 ++ 7 files changed, 313 insertions(+) create mode 100644 acceptance/openstack/identity/v3/limits_test.go create mode 100644 openstack/identity/v3/limits/doc.go create mode 100644 openstack/identity/v3/limits/requests.go create mode 100644 openstack/identity/v3/limits/results.go create mode 100644 openstack/identity/v3/limits/testing/fixtures.go create mode 100644 openstack/identity/v3/limits/testing/requests_test.go create mode 100644 openstack/identity/v3/limits/urls.go diff --git a/acceptance/openstack/identity/v3/limits_test.go b/acceptance/openstack/identity/v3/limits_test.go new file mode 100644 index 0000000000..1bb7b1e921 --- /dev/null +++ b/acceptance/openstack/identity/v3/limits_test.go @@ -0,0 +1,27 @@ +//go:build acceptance +// +build acceptance + +package v3 + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/openstack/identity/v3/limits" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestLimitsList(t *testing.T) { + clients.RequireAdmin(t) + + client, err := clients.NewIdentityV3Client() + th.AssertNoErr(t, err) + + listOpts := limits.ListOpts{} + + allPages, err := limits.List(client, listOpts).AllPages() + th.AssertNoErr(t, err) + + _, err = limits.ExtractLimits(allPages) + th.AssertNoErr(t, err) +} diff --git a/openstack/identity/v3/limits/doc.go b/openstack/identity/v3/limits/doc.go new file mode 100644 index 0000000000..7c390c3bf5 --- /dev/null +++ b/openstack/identity/v3/limits/doc.go @@ -0,0 +1,22 @@ +/* +Package limits provides information and interaction with limits for the +Openstack Identity service. + +Example to List Limits + + listOpts := limits.ListOpts{ + ProjectID: "3d596369fd2043bf8aca3c8decb0189e", + } + + allPages, err := limits.List(identityClient, listOpts).AllPages() + if err != nil { + panic(err) + } + + allLimits, err := limits.ExtractLimits(allPages) + if err != nil { + panic(err) + } + +*/ +package limits diff --git a/openstack/identity/v3/limits/requests.go b/openstack/identity/v3/limits/requests.go new file mode 100644 index 0000000000..f9e78e7339 --- /dev/null +++ b/openstack/identity/v3/limits/requests.go @@ -0,0 +1,51 @@ +package limits + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// ListOptsBuilder allows extensions to add additional parameters to +// the List request +type ListOptsBuilder interface { + ToLimitListQuery() (string, error) +} + +// ListOpts provides options to filter the List results. +type ListOpts struct { + // Filters the response by a region ID. + RegionID string `q:"region_id"` + + // Filters the response by a project ID. + ProjectID string `q:"project_id"` + + // Filters the response by a domain ID. + DomainID string `q:"domain_id"` + + // Filters the response by a service ID. + ServiceID string `q:"service_id"` + + // Filters the response by a resource name. + ResourceName string `q:"resource_name"` +} + +// ToLimitListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToLimitListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// List enumerates the limits. +func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := listURL(client) + if opts != nil { + query, err := opts.ToLimitListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return LimitPage{pagination.LinkedPageBase{PageResult: r}} + }) +} diff --git a/openstack/identity/v3/limits/results.go b/openstack/identity/v3/limits/results.go new file mode 100644 index 0000000000..ba57ad9dae --- /dev/null +++ b/openstack/identity/v3/limits/results.go @@ -0,0 +1,71 @@ +package limits + +import ( + "github.com/gophercloud/gophercloud/pagination" +) + +// A limit is the limit that override the registered limit for each project. +type Limit struct { + // ID is the unique ID of the limit. + ID string `json:"id"` + + // RegionID is the ID of the region where the limit is applied. + RegionID string `json:"region_id"` + + // ProjectID is the ID of the project where the limit is applied. + ProjectID string `json:"project_id"` + + // DomainID is the ID of the domain where the limit is applied. + DomainID string `json:"domain_id"` + + // ServiceID is the ID of the service where the limit is applied. + ServiceID string `json:"service_id"` + + // Description of the limit. + Description string `json:"description"` + + // ResourceName is the name of the resource that the limit is applied to. + ResourceName string `json:"resource_name"` + + // ResourceLimit is the override limit. + ResourceLimit int `json:"resource_limit"` + + // Links contains referencing links to the limit. + Links map[string]interface{} `json:"links"` +} + +// LimitPage is a single page of Limit results. +type LimitPage struct { + pagination.LinkedPageBase +} + +// IsEmpty determines whether or not a page of Limits contains any results. +func (r LimitPage) IsEmpty() (bool, error) { + limits, err := ExtractLimits(r) + return len(limits) == 0, err +} + +// NextPageURL extracts the "next" link from the links section of the result. +func (r LimitPage) 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 +} + +// ExtractLimits returns a slice of Limits contained in a single page of +// results. +func ExtractLimits(r pagination.Page) ([]Limit, error) { + var s struct { + Limits []Limit `json:"limits"` + } + err := (r.(LimitPage)).ExtractInto(&s) + return s.Limits, err +} diff --git a/openstack/identity/v3/limits/testing/fixtures.go b/openstack/identity/v3/limits/testing/fixtures.go new file mode 100644 index 0000000000..69ab12ad45 --- /dev/null +++ b/openstack/identity/v3/limits/testing/fixtures.go @@ -0,0 +1,93 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + "github.com/gophercloud/gophercloud/openstack/identity/v3/limits" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +// ListOutput provides a single page of List results. +const ListOutput = ` +{ + "links": { + "self": "http://10.3.150.25/identity/v3/limits", + "previous": null, + "next": null + }, + "limits": [ + { + "resource_name": "volume", + "region_id": null, + "links": { + "self": "http://10.3.150.25/identity/v3/limits/25a04c7a065c430590881c646cdcdd58" + }, + "service_id": "9408080f1970482aa0e38bc2d4ea34b7", + "project_id": "3a705b9f56bb439381b43c4fe59dccce", + "domain_id": null, + "id": "25a04c7a065c430590881c646cdcdd58", + "resource_limit": 11, + "description": "Number of volumes for project 3a705b9f56bb439381b43c4fe59dccce" + }, + { + "resource_name": "snapshot", + "region_id": "RegionOne", + "links": { + "self": "http://10.3.150.25/identity/v3/limits/3229b3849f584faea483d6851f7aab05" + }, + "service_id": "9408080f1970482aa0e38bc2d4ea34b7", + "project_id": "3a705b9f56bb439381b43c4fe59dccce", + "domain_id": null, + "id": "3229b3849f584faea483d6851f7aab05", + "resource_limit": 5, + "description": null + } + ] +} +` + +// FirstLimit is the first limit in the List request. +var FirstLimit = limits.Limit{ + ResourceName: "volume", + Links: map[string]interface{}{ + "self": "http://10.3.150.25/identity/v3/limits/25a04c7a065c430590881c646cdcdd58", + }, + ServiceID: "9408080f1970482aa0e38bc2d4ea34b7", + ProjectID: "3a705b9f56bb439381b43c4fe59dccce", + ID: "25a04c7a065c430590881c646cdcdd58", + ResourceLimit: 11, + Description: "Number of volumes for project 3a705b9f56bb439381b43c4fe59dccce", +} + +// SecondLimit is the second limit in the List request. +var SecondLimit = limits.Limit{ + ResourceName: "snapshot", + RegionID: "RegionOne", + Links: map[string]interface{}{ + "self": "http://10.3.150.25/identity/v3/limits/3229b3849f584faea483d6851f7aab05", + }, + ServiceID: "9408080f1970482aa0e38bc2d4ea34b7", + ProjectID: "3a705b9f56bb439381b43c4fe59dccce", + ID: "3229b3849f584faea483d6851f7aab05", + ResourceLimit: 5, +} + +// ExpectedLimitsSlice is the slice of limits expected to be returned from ListOutput. +var ExpectedLimitsSlice = []limits.Limit{FirstLimit, SecondLimit} + +// HandleListLimitsSuccessfully creates an HTTP handler at `/limits` on the +// test handler mux that responds with a list of two limits. +func HandleListLimitsSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/limits", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "Accept", "application/json") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, ListOutput) + }) +} diff --git a/openstack/identity/v3/limits/testing/requests_test.go b/openstack/identity/v3/limits/testing/requests_test.go new file mode 100644 index 0000000000..42e55d691a --- /dev/null +++ b/openstack/identity/v3/limits/testing/requests_test.go @@ -0,0 +1,42 @@ +package testing + +import ( + "testing" + + "github.com/gophercloud/gophercloud/openstack/identity/v3/limits" + "github.com/gophercloud/gophercloud/pagination" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestListLimits(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListLimitsSuccessfully(t) + + count := 0 + err := limits.List(client.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) { + count++ + + actual, err := limits.ExtractLimits(page) + th.AssertNoErr(t, err) + + th.CheckDeepEquals(t, ExpectedLimitsSlice, actual) + + return true, nil + }) + th.AssertNoErr(t, err) + th.CheckEquals(t, count, 1) +} + +func TestListLimitsAllPages(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListLimitsSuccessfully(t) + + allPages, err := limits.List(client.ServiceClient(), nil).AllPages() + th.AssertNoErr(t, err) + actual, err := limits.ExtractLimits(allPages) + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, ExpectedLimitsSlice, actual) +} diff --git a/openstack/identity/v3/limits/urls.go b/openstack/identity/v3/limits/urls.go new file mode 100644 index 0000000000..1892614541 --- /dev/null +++ b/openstack/identity/v3/limits/urls.go @@ -0,0 +1,7 @@ +package limits + +import "github.com/gophercloud/gophercloud" + +func listURL(client *gophercloud.ServiceClient) string { + return client.ServiceURL("limits") +} From 99f7526d11e34a249eb9bf1aa7cfdd577019100e Mon Sep 17 00:00:00 2001 From: Emilien Macchi <emilien@redhat.com> Date: Fri, 25 Feb 2022 10:55:07 -0500 Subject: [PATCH 056/360] Adding CI job for testing sharedfilesystems (Manila) --- .../functional-sharedfilesystems.yaml | 73 +++++++++++++++++++ .../v2/messages/messages_test.go | 12 --- .../sharedfilesystems/v2/shares_test.go | 35 --------- .../sharedfilesystems/v2/snapshots_test.go | 9 --- 4 files changed, 73 insertions(+), 56 deletions(-) create mode 100644 .github/workflows/functional-sharedfilesystems.yaml diff --git a/.github/workflows/functional-sharedfilesystems.yaml b/.github/workflows/functional-sharedfilesystems.yaml new file mode 100644 index 0000000000..92b1e94383 --- /dev/null +++ b/.github/workflows/functional-sharedfilesystems.yaml @@ -0,0 +1,73 @@ +name: functional-sharedfilesystems +on: + pull_request: + paths: + - '**sharedfilesystems**' + schedule: + - cron: '0 0 * * *' +jobs: + functional-sharedfilesystems: + strategy: + fail-fast: false + matrix: + name: ["master"] + openstack_version: ["master"] + ubuntu_version: ["20.04"] + include: + - name: "xena" + openstack_version: "stable/xena" + ubuntu_version: "20.04" + - name: "wallaby" + openstack_version: "stable/wallaby" + ubuntu_version: "20.04" + - name: "victoria" + openstack_version: "stable/victoria" + ubuntu_version: "20.04" + - name: "ussuri" + openstack_version: "stable/ussuri" + ubuntu_version: "18.04" + - name: "train" + openstack_version: "stable/train" + ubuntu_version: "18.04" + runs-on: ubuntu-${{ matrix.ubuntu_version }} + name: Deploy OpenStack ${{ matrix.name }} with Manila and run sharedfilesystems acceptance tests + steps: + - name: Checkout Gophercloud + uses: actions/checkout@v2 + - name: Deploy devstack + uses: EmilienM/devstack-action@v0.6 + with: + branch: ${{ matrix.openstack_version }} + conf_overrides: | + enable_plugin manila https://opendev.org/openstack/manila ${{ matrix.openstack_version }} + # LVM Backend config options + MANILA_SERVICE_IMAGE_ENABLED=False + SHARE_DRIVER=manila.share.drivers.lvm.LVMShareDriver + MANILA_ENABLED_BACKENDS=chicago,denver + MANILA_BACKEND1_CONFIG_GROUP_NAME=chicago + MANILA_BACKEND2_CONFIG_GROUP_NAME=denver + MANILA_SHARE_BACKEND1_NAME=CHICAGO + MANILA_SHARE_BACKEND2_NAME=DENVER + MANILA_OPTGROUP_chicago_driver_handles_share_servers=False + MANILA_OPTGROUP_denver_driver_handles_share_servers=False + SHARE_BACKING_FILE_SIZE=32000M + MANILA_DEFAULT_SHARE_TYPE_EXTRA_SPECS='snapshot_support=True create_share_from_snapshot_support=True revert_to_snapshot_support=True mount_snapshot_support=True' + MANILA_CONFIGURE_DEFAULT_TYPES=True + - name: Checkout go + uses: actions/setup-go@v2 + with: + go-version: '^1.15' + - name: Run Gophercloud acceptance tests + run: ./script/acceptancetest + env: + DEVSTACK_PATH: ${{ github.workspace }}/devstack + ACCEPTANCE_TESTS_FILTER: "^.*sharedfilesystems.*$" + - name: Generate logs on failure + run: ./script/collectlogs + if: failure() + - name: Upload logs artifacts on failure + if: failure() + uses: actions/upload-artifact@v2 + with: + name: functional-sharedfilesystems-${{ matrix.name }} + path: /tmp/devstack-logs/* diff --git a/acceptance/openstack/sharedfilesystems/v2/messages/messages_test.go b/acceptance/openstack/sharedfilesystems/v2/messages/messages_test.go index be3c4207e4..d3e65603d9 100644 --- a/acceptance/openstack/sharedfilesystems/v2/messages/messages_test.go +++ b/acceptance/openstack/sharedfilesystems/v2/messages/messages_test.go @@ -12,10 +12,6 @@ const requestID = "req-6f52cd8b-25a1-42cf-b497-7babf70f55f4" const minimumManilaMessagesMicroVersion = "2.37" func TestMessageList(t *testing.T) { - clients.SkipRelease(t, "stable/mitaka") - clients.SkipRelease(t, "stable/newton") - clients.SkipRelease(t, "stable/ocata") - client, err := clients.NewSharedFileSystemV2Client() if err != nil { t.Fatalf("Unable to create a shared file system client: %v", err) @@ -40,10 +36,6 @@ func TestMessageList(t *testing.T) { // The test creates 2 messages and verifies that only the one(s) with // a particular name are being listed func TestMessageListFiltering(t *testing.T) { - clients.SkipRelease(t, "stable/mitaka") - clients.SkipRelease(t, "stable/newton") - clients.SkipRelease(t, "stable/ocata") - client, err := clients.NewSharedFileSystemV2Client() if err != nil { t.Fatalf("Unable to create a shared file system client: %v", err) @@ -75,10 +67,6 @@ func TestMessageListFiltering(t *testing.T) { // Create a message and update the name and description. Get the ity // service and verify that the name and description have been updated func TestMessageDelete(t *testing.T) { - clients.SkipRelease(t, "stable/mitaka") - clients.SkipRelease(t, "stable/newton") - clients.SkipRelease(t, "stable/ocata") - client, err := clients.NewSharedFileSystemV2Client() if err != nil { t.Fatalf("Unable to create shared file system client: %v", err) diff --git a/acceptance/openstack/sharedfilesystems/v2/shares_test.go b/acceptance/openstack/sharedfilesystems/v2/shares_test.go index 87fd8e3c2b..208e8993f6 100644 --- a/acceptance/openstack/sharedfilesystems/v2/shares_test.go +++ b/acceptance/openstack/sharedfilesystems/v2/shares_test.go @@ -13,9 +13,6 @@ import ( ) func TestShareCreate(t *testing.T) { - clients.SkipRelease(t, "stable/mitaka") - clients.SkipRelease(t, "stable/newton") - client, err := clients.NewSharedFileSystemV2Client() if err != nil { t.Fatalf("Unable to create a shared file system client: %v", err) @@ -36,9 +33,6 @@ func TestShareCreate(t *testing.T) { } func TestShareExportLocations(t *testing.T) { - clients.SkipRelease(t, "stable/mitaka") - clients.SkipRelease(t, "stable/newton") - client, err := clients.NewSharedFileSystemV2Client() if err != nil { t.Fatalf("Unable to create a shared file system client: %v", err) @@ -73,9 +67,6 @@ func TestShareExportLocations(t *testing.T) { } func TestShareUpdate(t *testing.T) { - clients.SkipRelease(t, "stable/mitaka") - clients.SkipRelease(t, "stable/newton") - client, err := clients.NewSharedFileSystemV2Client() if err != nil { t.Fatalf("Unable to create shared file system client: %v", err) @@ -125,9 +116,6 @@ func TestShareUpdate(t *testing.T) { } func TestShareListDetail(t *testing.T) { - clients.SkipRelease(t, "stable/mitaka") - clients.SkipRelease(t, "stable/newton") - client, err := clients.NewSharedFileSystemV2Client() if err != nil { t.Fatalf("Unable to create a shared file system client: %v", err) @@ -151,9 +139,6 @@ func TestShareListDetail(t *testing.T) { } func TestGrantAndRevokeAccess(t *testing.T) { - clients.SkipRelease(t, "stable/mitaka") - clients.SkipRelease(t, "stable/newton") - client, err := clients.NewSharedFileSystemV2Client() if err != nil { t.Fatalf("Unable to create a shared file system client: %v", err) @@ -180,9 +165,6 @@ func TestGrantAndRevokeAccess(t *testing.T) { } func TestListAccessRights(t *testing.T) { - clients.SkipRelease(t, "stable/mitaka") - clients.SkipRelease(t, "stable/newton") - client, err := clients.NewSharedFileSystemV2Client() if err != nil { t.Fatalf("Unable to create a shared file system client: %v", err) @@ -218,9 +200,6 @@ func TestListAccessRights(t *testing.T) { } func TestExtendAndShrink(t *testing.T) { - clients.SkipRelease(t, "stable/mitaka") - clients.SkipRelease(t, "stable/newton") - client, err := clients.NewSharedFileSystemV2Client() if err != nil { t.Fatalf("Unable to create a shared file system client: %v", err) @@ -264,9 +243,6 @@ func TestExtendAndShrink(t *testing.T) { } func TestShareMetadata(t *testing.T) { - clients.SkipRelease(t, "stable/mitaka") - clients.SkipRelease(t, "stable/newton") - client, err := clients.NewSharedFileSystemV2Client() if err != nil { t.Fatalf("Unable to create a shared file system client: %v", err) @@ -326,9 +302,6 @@ func TestShareMetadata(t *testing.T) { } func TestRevert(t *testing.T) { - clients.SkipRelease(t, "stable/mitaka") - clients.SkipRelease(t, "stable/newton") - client, err := clients.NewSharedFileSystemV2Client() if err != nil { t.Fatalf("Unable to create a shared file system client: %v", err) @@ -381,9 +354,6 @@ func TestRevert(t *testing.T) { } func TestResetStatus(t *testing.T) { - clients.SkipRelease(t, "stable/mitaka") - clients.SkipRelease(t, "stable/newton") - client, err := clients.NewSharedFileSystemV2Client() if err != nil { t.Fatalf("Unable to create a shared file system client: %v", err) @@ -420,9 +390,6 @@ func TestResetStatus(t *testing.T) { } func TestForceDelete(t *testing.T) { - clients.SkipRelease(t, "stable/mitaka") - clients.SkipRelease(t, "stable/newton") - client, err := clients.NewSharedFileSystemV2Client() if err != nil { t.Fatalf("Unable to create a shared file system client: %v", err) @@ -455,8 +422,6 @@ func TestForceDelete(t *testing.T) { } func TestUnmanage(t *testing.T) { - clients.SkipRelease(t, "stable/mitaka") - clients.SkipRelease(t, "stable/newton") clients.RequireAdmin(t) client, err := clients.NewSharedFileSystemV2Client() diff --git a/acceptance/openstack/sharedfilesystems/v2/snapshots_test.go b/acceptance/openstack/sharedfilesystems/v2/snapshots_test.go index 190534dc3d..a30bad98bb 100644 --- a/acceptance/openstack/sharedfilesystems/v2/snapshots_test.go +++ b/acceptance/openstack/sharedfilesystems/v2/snapshots_test.go @@ -13,9 +13,6 @@ import ( ) func TestSnapshotCreate(t *testing.T) { - clients.SkipRelease(t, "stable/mitaka") - clients.SkipRelease(t, "stable/newton") - client, err := clients.NewSharedFileSystemV2Client() if err != nil { t.Fatalf("Unable to create a shared file system client: %v", err) @@ -44,9 +41,6 @@ func TestSnapshotCreate(t *testing.T) { } func TestSnapshotUpdate(t *testing.T) { - clients.SkipRelease(t, "stable/mitaka") - clients.SkipRelease(t, "stable/newton") - client, err := clients.NewSharedFileSystemV2Client() if err != nil { t.Fatalf("Unable to create shared file system client: %v", err) @@ -97,9 +91,6 @@ func TestSnapshotUpdate(t *testing.T) { } func TestSnapshotListDetail(t *testing.T) { - clients.SkipRelease(t, "stable/mitaka") - clients.SkipRelease(t, "stable/newton") - client, err := clients.NewSharedFileSystemV2Client() if err != nil { t.Fatalf("Unable to create a shared file system client: %v", err) From 391e85e4fecc0ce8a6135e9a0345246b45d47574 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= <martin.andre@gmail.com> Date: Fri, 25 Feb 2022 22:59:53 +0100 Subject: [PATCH 057/360] zuul: drop storage jobs We've replaced all of the storage jobs with github actions. --- .zuul.yaml | 113 ----------------------------------------------------- 1 file changed, 113 deletions(-) diff --git a/.zuul.yaml b/.zuul.yaml index 8bf374cf04..99944fc100 100644 --- a/.zuul.yaml +++ b/.zuul.yaml @@ -42,28 +42,6 @@ - heat - zun -- job: - name: gophercloud-acceptance-test-storage - parent: gophercloud-acceptance-test-base - description: | - Run gophercloud storage acceptance test on master branch - files: - - ^.*blockstorage.*$ - - ^.*imageservice.*$ - - ^.*objectstorage.*$ - - ^.*sharedfilesystems.*$ - vars: - acceptance_tests: - - acceptance/openstack/blockstorage - - acceptance/openstack/blockstorage/extensions - - acceptance/openstack/blockstorage/v3 - - acceptance/openstack/imageservice/v2 - - acceptance/openstack/objectstorage/v1 - - acceptance/openstack/sharedfilesystems/v2 - - acceptance/openstack/sharedfilesystems/v2/messages - devstack_services: - - manila - - job: name: gophercloud-acceptance-test-compute-ussuri parent: gophercloud-acceptance-test-compute @@ -74,16 +52,6 @@ global_env: OS_BRANCH: stable/ussuri -- job: - name: gophercloud-acceptance-test-storage-ussuri - parent: gophercloud-acceptance-test-storage - nodeset: ubuntu-bionic - description: | - Run gophercloud storage test on ussuri branch - vars: - global_env: - OS_BRANCH: stable/ussuri - - job: name: gophercloud-acceptance-test-compute-train parent: gophercloud-acceptance-test-compute @@ -94,16 +62,6 @@ global_env: OS_BRANCH: stable/train -- job: - name: gophercloud-acceptance-test-storage-train - parent: gophercloud-acceptance-test-storage - nodeset: ubuntu-xenial - description: | - Run gophercloud storage acceptance test on train branch - vars: - global_env: - OS_BRANCH: stable/train - - job: name: gophercloud-acceptance-test-compute-stein parent: gophercloud-acceptance-test-compute @@ -114,16 +72,6 @@ global_env: OS_BRANCH: stable/stein -- job: - name: gophercloud-acceptance-test-storage-stein - parent: gophercloud-acceptance-test-storage - nodeset: ubuntu-xenial - description: | - Run gophercloud storage acceptance test on stein branch - vars: - global_env: - OS_BRANCH: stable/stein - - job: name: gophercloud-acceptance-test-compute-rocky parent: gophercloud-acceptance-test-compute @@ -134,16 +82,6 @@ global_env: OS_BRANCH: stable/rocky -- job: - name: gophercloud-acceptance-test-storage-rocky - parent: gophercloud-acceptance-test-storage - nodeset: ubuntu-xenial - description: | - Run gophercloud storage acceptance test on rocky branch - vars: - global_env: - OS_BRANCH: stable/rocky - - job: name: gophercloud-acceptance-test-compute-queens parent: gophercloud-acceptance-test-compute @@ -154,16 +92,6 @@ global_env: OS_BRANCH: stable/queens -- job: - name: gophercloud-acceptance-test-storage-queens - parent: gophercloud-acceptance-test-storage - description: | - Run gophercloud storage acceptance test on queens branch - nodeset: ubuntu-xenial - vars: - global_env: - OS_BRANCH: stable/queens - # NOTE: A Pike-based devstack environment is currently # not building correctly. This might be a temporary issue. - job: @@ -176,16 +104,6 @@ global_env: OS_BRANCH: stable/pike -- job: - name: gophercloud-acceptance-test-storage-pike - parent: gophercloud-acceptance-test-storage - description: | - Run gophercloud storage acceptance test on pike branch - nodeset: ubuntu-xenial - vars: - global_env: - OS_BRANCH: stable/pike - - job: name: gophercloud-acceptance-test-compute-ocata parent: gophercloud-acceptance-test-compute @@ -196,16 +114,6 @@ global_env: OS_BRANCH: stable/ocata -- job: - name: gophercloud-acceptance-test-storage-ocata - parent: gophercloud-acceptance-test-storage - description: | - Run gophercloud storage acceptance test on ocata branch - nodeset: ubuntu-xenial - vars: - global_env: - OS_BRANCH: stable/ocata - # NOTE: A Newton-based devstack environment is currently # not building correctly. This might be a temporary issue. - job: @@ -218,16 +126,6 @@ global_env: OS_BRANCH: stable/newton -- job: - name: gophercloud-acceptance-test-storage-newton - parent: gophercloud-acceptance-test-storage - description: | - Run gophercloud storage acceptance test on newton branch - nodeset: ubuntu-xenial - vars: - global_env: - OS_BRANCH: stable/newton - # The following jobs are maintained because they are parents # for gophercloud v0.4.0 acceptance tests in theopenlab/openlab-zuul-jobs. # It'll be removed once we finished the migration to Github actions. @@ -244,8 +142,6 @@ - acceptance/openstack/container/v1 - acceptance/openstack/orchestration/v1 - acceptance/openstack/placement/v1 - - acceptance/openstack/sharedfilesystems/v2 - - acceptance/openstack/sharedfilesystems/v2/messages devstack_services: - manila - zun @@ -320,36 +216,27 @@ jobs: - gophercloud-unittest - gophercloud-acceptance-test-compute - - gophercloud-acceptance-test-storage recheck-newton: jobs: - gophercloud-acceptance-test-compute-newton - - gophercloud-acceptance-test-storage-newton recheck-ocata: jobs: - gophercloud-acceptance-test-compute-ocata - - gophercloud-acceptance-test-storage-ocata recheck-pike: jobs: - gophercloud-acceptance-test-compute-pike - - gophercloud-acceptance-test-storage-pike recheck-queens: jobs: - gophercloud-acceptance-test-compute-queens - - gophercloud-acceptance-test-storage-queens recheck-rocky: jobs: - gophercloud-acceptance-test-compute-rocky - - gophercloud-acceptance-test-storage-rocky recheck-stein: jobs: - gophercloud-acceptance-test-compute-stein - - gophercloud-acceptance-test-storage-stein recheck-train: jobs: - gophercloud-acceptance-test-compute-train - - gophercloud-acceptance-test-storage-train recheck-ussuri: jobs: - gophercloud-acceptance-test-compute-ussuri - - gophercloud-acceptance-test-storage-ussuri From 4e166468e1faa1c610d86dc9129d9b1eeb55e603 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= <martin.andre@gmail.com> Date: Sat, 26 Feb 2022 11:56:55 +0100 Subject: [PATCH 058/360] [CI-v2] Add placement job with Github Action * Deploy Placement with Devstack * Run placement acceptance tests * Logs failures if any --- .github/workflows/functional-placement.yaml | 58 +++++++++++++++++++ .../placement/v1/resourceproviders_test.go | 25 -------- 2 files changed, 58 insertions(+), 25 deletions(-) create mode 100644 .github/workflows/functional-placement.yaml diff --git a/.github/workflows/functional-placement.yaml b/.github/workflows/functional-placement.yaml new file mode 100644 index 0000000000..2b46e7ead6 --- /dev/null +++ b/.github/workflows/functional-placement.yaml @@ -0,0 +1,58 @@ +name: functional-placement +on: + pull_request: + paths: + - '**placement**' + schedule: + - cron: '0 0 * * *' +jobs: + functional-placement: + strategy: + fail-fast: false + matrix: + name: ["master"] + openstack_version: ["master"] + ubuntu_version: ["20.04"] + include: + - name: "xena" + openstack_version: "stable/xena" + ubuntu_version: "20.04" + - name: "wallaby" + openstack_version: "stable/wallaby" + ubuntu_version: "20.04" + - name: "victoria" + openstack_version: "stable/victoria" + ubuntu_version: "20.04" + - name: "ussuri" + openstack_version: "stable/ussuri" + ubuntu_version: "18.04" + - name: "train" + openstack_version: "stable/train" + ubuntu_version: "18.04" + runs-on: ubuntu-${{ matrix.ubuntu_version }} + name: Deploy OpenStack ${{ matrix.name }} with Placement and run placement acceptance tests + steps: + - name: Checkout Gophercloud + uses: actions/checkout@v2 + - name: Deploy devstack + uses: EmilienM/devstack-action@v0.6 + with: + branch: ${{ matrix.openstack_version }} + - name: Checkout go + uses: actions/setup-go@v2 + with: + go-version: '^1.15' + - name: Run Gophercloud acceptance tests + run: ./script/acceptancetest + env: + DEVSTACK_PATH: ${{ github.workspace }}/devstack + ACCEPTANCE_TESTS_FILTER: ^.*placement.*$ + - name: Generate logs on failure + run: ./script/collectlogs + if: failure() + - name: Upload logs artifacts on failure + if: failure() + uses: actions/upload-artifact@v2 + with: + name: functional-placement-${{ matrix.name }} + path: /tmp/devstack-logs/* diff --git a/acceptance/openstack/placement/v1/resourceproviders_test.go b/acceptance/openstack/placement/v1/resourceproviders_test.go index 0092b10ef1..41dcbbac89 100644 --- a/acceptance/openstack/placement/v1/resourceproviders_test.go +++ b/acceptance/openstack/placement/v1/resourceproviders_test.go @@ -27,11 +27,6 @@ func TestResourceProviderList(t *testing.T) { } func TestResourceProviderCreate(t *testing.T) { - clients.SkipRelease(t, "stable/mitaka") - clients.SkipRelease(t, "stable/newton") - clients.SkipRelease(t, "stable/ocata") - clients.SkipRelease(t, "stable/pike") - clients.SkipRelease(t, "stable/queens") clients.RequireAdmin(t) client, err := clients.NewPlacementV1Client() @@ -52,11 +47,6 @@ func TestResourceProviderCreate(t *testing.T) { } func TestResourceProviderUsages(t *testing.T) { - clients.SkipRelease(t, "stable/mitaka") - clients.SkipRelease(t, "stable/newton") - clients.SkipRelease(t, "stable/ocata") - clients.SkipRelease(t, "stable/pike") - clients.SkipRelease(t, "stable/queens") clients.RequireAdmin(t) clients.RequireAdmin(t) @@ -84,11 +74,6 @@ func TestResourceProviderUsages(t *testing.T) { } func TestResourceProviderInventories(t *testing.T) { - clients.SkipRelease(t, "stable/mitaka") - clients.SkipRelease(t, "stable/newton") - clients.SkipRelease(t, "stable/ocata") - clients.SkipRelease(t, "stable/pike") - clients.SkipRelease(t, "stable/queens") clients.RequireAdmin(t) client, err := clients.NewPlacementV1Client() @@ -114,11 +99,6 @@ func TestResourceProviderInventories(t *testing.T) { } func TestResourceProviderTraits(t *testing.T) { - clients.SkipRelease(t, "stable/mitaka") - clients.SkipRelease(t, "stable/newton") - clients.SkipRelease(t, "stable/ocata") - clients.SkipRelease(t, "stable/pike") - clients.SkipRelease(t, "stable/queens") clients.RequireAdmin(t) client, err := clients.NewPlacementV1Client() @@ -144,11 +124,6 @@ func TestResourceProviderTraits(t *testing.T) { } func TestResourceProviderAllocations(t *testing.T) { - clients.SkipRelease(t, "stable/mitaka") - clients.SkipRelease(t, "stable/newton") - clients.SkipRelease(t, "stable/ocata") - clients.SkipRelease(t, "stable/pike") - clients.SkipRelease(t, "stable/queens") clients.RequireAdmin(t) client, err := clients.NewPlacementV1Client() From 9971790747e8a9e628d665004c5a83fe12247d64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= <martin.andre@gmail.com> Date: Sat, 26 Feb 2022 18:34:17 +0100 Subject: [PATCH 059/360] Fix trigger of functional-dns job The previous filter would not run the functional-dns job when files touching the DNS service, under the openstack/dns path, were modified. This commit fixes it. We can't use the simpler `**dns**` filter as this would match the files for the neutron DNS extension which is a different thing. --- .github/workflows/functional-dns.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/functional-dns.yaml b/.github/workflows/functional-dns.yaml index a72fbda03d..8fd78bfe9d 100644 --- a/.github/workflows/functional-dns.yaml +++ b/.github/workflows/functional-dns.yaml @@ -2,7 +2,7 @@ name: functional-dns on: pull_request: paths: - - 'acceptance/openstack/dns**' + - '**openstack/dns**' - '**functional-dns.yaml' schedule: - cron: '0 0 * * *' From 2623777cfb7fbb0b3fdee6fcd6bd6fcee9e9d056 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sandro=20J=C3=A4ckel?= <sandro.jaeckel@sap.com> Date: Tue, 15 Feb 2022 16:27:05 +0100 Subject: [PATCH 060/360] compute/v2/extensions/servergroups: add user_id and project_id to server_groups --- openstack/compute/v2/extensions/servergroups/results.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/openstack/compute/v2/extensions/servergroups/results.go b/openstack/compute/v2/extensions/servergroups/results.go index de41f12304..c8c5b65c81 100644 --- a/openstack/compute/v2/extensions/servergroups/results.go +++ b/openstack/compute/v2/extensions/servergroups/results.go @@ -29,6 +29,12 @@ type ServerGroup struct { // Members are the members of the server group. Members []string `json:"members"` + // UserID of the server group. + UserID string `json:"user_id"` + + // ProjectID of the server group. + ProjectID string `json:"project_id"` + // Metadata includes a list of all user-specified key-value pairs attached // to the Server Group. Metadata map[string]interface{} From ddfa463d521d3bc87095d41f7f59de054d3667a8 Mon Sep 17 00:00:00 2001 From: nikParasyr <nik.parasyr@protonmail.com> Date: Wed, 23 Feb 2022 12:51:01 +0100 Subject: [PATCH 061/360] Add ParentProviderUUID to resourceProvider Add ParentProviderUUID to placement/v1/resourceproviders createOpts. Moreover restructure acceptance tests for consistency and for future additions. [Docs](https://docs.openstack.org/api-ref/placement/?expanded=create-resource-provider-detail) Relates to: #526 --- .../openstack/placement/v1/placement.go | 56 +++++++++++++++++++ .../placement/v1/resourceproviders_test.go | 53 +++--------------- .../placement/v1/resourceproviders/doc.go | 1 + .../v1/resourceproviders/requests.go | 3 + .../testing/requests_test.go | 5 +- 5 files changed, 70 insertions(+), 48 deletions(-) create mode 100644 acceptance/openstack/placement/v1/placement.go diff --git a/acceptance/openstack/placement/v1/placement.go b/acceptance/openstack/placement/v1/placement.go new file mode 100644 index 0000000000..079fe6c3f4 --- /dev/null +++ b/acceptance/openstack/placement/v1/placement.go @@ -0,0 +1,56 @@ +package v1 + +import ( + "testing" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/placement/v1/resourceproviders" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func CreateResourceProvider(t *testing.T, client *gophercloud.ServiceClient) (*resourceproviders.ResourceProvider, error) { + name := tools.RandomString("TESTACC-", 8) + t.Logf("Attempting to create resource provider: %s", name) + + createOpts := resourceproviders.CreateOpts{ + Name: name, + } + + client.Microversion = "1.20" + resourceProvider, err := resourceproviders.Create(client, createOpts).Extract() + if err != nil { + return resourceProvider, err + } + + t.Logf("Successfully created resourceProvider: %s.", resourceProvider.Name) + tools.PrintResource(t, resourceProvider) + + th.AssertEquals(t, resourceProvider.Name, name) + + return resourceProvider, nil +} + +func CreateResourceProviderWithParent(t *testing.T, client *gophercloud.ServiceClient, parentUUID string) (*resourceproviders.ResourceProvider, error) { + name := tools.RandomString("TESTACC-", 8) + t.Logf("Attempting to create resource provider: %s", name) + + createOpts := resourceproviders.CreateOpts{ + Name: name, + ParentProviderUUID: parentUUID, + } + + client.Microversion = "1.20" + resourceProvider, err := resourceproviders.Create(client, createOpts).Extract() + if err != nil { + return resourceProvider, err + } + + t.Logf("Successfully created resourceProvider: %s.", resourceProvider.Name) + tools.PrintResource(t, resourceProvider) + + th.AssertEquals(t, resourceProvider.Name, name) + th.AssertEquals(t, resourceProvider.ParentProviderUUID, parentUUID) + + return resourceProvider, nil +} diff --git a/acceptance/openstack/placement/v1/resourceproviders_test.go b/acceptance/openstack/placement/v1/resourceproviders_test.go index 41dcbbac89..0a946f4572 100644 --- a/acceptance/openstack/placement/v1/resourceproviders_test.go +++ b/acceptance/openstack/placement/v1/resourceproviders_test.go @@ -32,18 +32,11 @@ func TestResourceProviderCreate(t *testing.T) { client, err := clients.NewPlacementV1Client() th.AssertNoErr(t, err) - name := tools.RandomString("TESTACC-", 8) - t.Logf("Attempting to create resource provider: %s", name) - - createOpts := resourceproviders.CreateOpts{ - Name: name, - } - - client.Microversion = "1.20" - resourceProvider, err := resourceproviders.Create(client, createOpts).Extract() + resourceProvider, err := CreateResourceProvider(t, client) th.AssertNoErr(t, err) - tools.PrintResource(t, resourceProvider) + resourceProvider, err = CreateResourceProviderWithParent(t, client, resourceProvider.UUID) + th.AssertNoErr(t, err) } func TestResourceProviderUsages(t *testing.T) { @@ -55,15 +48,7 @@ func TestResourceProviderUsages(t *testing.T) { th.AssertNoErr(t, err) // first create new resource provider - name := tools.RandomString("TESTACC-", 8) - t.Logf("Attempting to create resource provider: %s", name) - - createOpts := resourceproviders.CreateOpts{ - Name: name, - } - - client.Microversion = "1.20" - resourceProvider, err := resourceproviders.Create(client, createOpts).Extract() + resourceProvider, err := CreateResourceProvider(t, client) th.AssertNoErr(t, err) // now get the usages for the newly created resource provider @@ -80,15 +65,7 @@ func TestResourceProviderInventories(t *testing.T) { th.AssertNoErr(t, err) // first create new resource provider - name := tools.RandomString("TESTACC-", 8) - t.Logf("Attempting to create resource provider: %s", name) - - createOpts := resourceproviders.CreateOpts{ - Name: name, - } - - client.Microversion = "1.20" - resourceProvider, err := resourceproviders.Create(client, createOpts).Extract() + resourceProvider, err := CreateResourceProvider(t, client) th.AssertNoErr(t, err) // now get the inventories for the newly created resource provider @@ -105,15 +82,7 @@ func TestResourceProviderTraits(t *testing.T) { th.AssertNoErr(t, err) // first create new resource provider - name := tools.RandomString("TESTACC-", 8) - t.Logf("Attempting to create resource provider: %s", name) - - createOpts := resourceproviders.CreateOpts{ - Name: name, - } - - client.Microversion = "1.20" - resourceProvider, err := resourceproviders.Create(client, createOpts).Extract() + resourceProvider, err := CreateResourceProvider(t, client) th.AssertNoErr(t, err) // now get the traits for the newly created resource provider @@ -130,15 +99,7 @@ func TestResourceProviderAllocations(t *testing.T) { th.AssertNoErr(t, err) // first create new resource provider - name := tools.RandomString("TESTACC-", 8) - t.Logf("Attempting to create resource provider: %s", name) - - createOpts := resourceproviders.CreateOpts{ - Name: name, - } - - client.Microversion = "1.20" - resourceProvider, err := resourceproviders.Create(client, createOpts).Extract() + resourceProvider, err := CreateResourceProvider(t, client) th.AssertNoErr(t, err) // now get the allocations for the newly created resource provider diff --git a/openstack/placement/v1/resourceproviders/doc.go b/openstack/placement/v1/resourceproviders/doc.go index 1945958438..236ee52b22 100644 --- a/openstack/placement/v1/resourceproviders/doc.go +++ b/openstack/placement/v1/resourceproviders/doc.go @@ -22,6 +22,7 @@ Example to create resource providers createOpts := resourceproviders.CreateOpts{ Name: "new-rp", UUID: "b99b3ab4-3aa6-4fba-b827-69b88b9c544a", + ParentProvider: "c7f50b40-6f32-4d7a-9f32-9384057be83b" } rp, err := resourceproviders.Create(placementClient, createOpts).Extract() diff --git a/openstack/placement/v1/resourceproviders/requests.go b/openstack/placement/v1/resourceproviders/requests.go index c2c9980838..a8b54e3972 100644 --- a/openstack/placement/v1/resourceproviders/requests.go +++ b/openstack/placement/v1/resourceproviders/requests.go @@ -69,6 +69,9 @@ type CreateOptsBuilder interface { type CreateOpts struct { Name string `json:"name"` UUID string `json:"uuid,omitempty"` + // The UUID of the immediate parent of the resource provider. + // Available in version >= 1.14 + ParentProviderUUID string `json:"parent_provider_uuid,omitempty"` } // ToResourceProviderCreateMap constructs a request body from CreateOpts. diff --git a/openstack/placement/v1/resourceproviders/testing/requests_test.go b/openstack/placement/v1/resourceproviders/testing/requests_test.go index 896df74812..6eba22a560 100644 --- a/openstack/placement/v1/resourceproviders/testing/requests_test.go +++ b/openstack/placement/v1/resourceproviders/testing/requests_test.go @@ -46,8 +46,9 @@ func TestCreateResourceProvider(t *testing.T) { expected := ExpectedResourceProvider1 opts := resourceproviders.CreateOpts{ - Name: ExpectedResourceProvider1.Name, - UUID: ExpectedResourceProvider1.UUID, + Name: ExpectedResourceProvider1.Name, + UUID: ExpectedResourceProvider1.UUID, + ParentProviderUUID: ExpectedResourceProvider1.ParentProviderUUID, } actual, err := resourceproviders.Create(fake.ServiceClient(), opts).Extract() From 78b1b960a2bd346643ec902d4dcf9946963a0f57 Mon Sep 17 00:00:00 2001 From: nikParasyr <nik.parasyr@protonmail.com> Date: Wed, 23 Feb 2022 14:20:04 +0100 Subject: [PATCH 062/360] Add Delete to placement resourceproviders [Docs](https://docs.openstack.org/api-ref/placement/?expanded=delete-resource-provider-detail) Relates to: #526 --- acceptance/openstack/placement/v1/placement.go | 14 ++++++++++++++ .../placement/v1/resourceproviders_test.go | 6 ++++++ openstack/placement/v1/resourceproviders/doc.go | 9 +++++++++ .../placement/v1/resourceproviders/requests.go | 7 +++++++ .../placement/v1/resourceproviders/results.go | 6 ++++++ .../v1/resourceproviders/testing/fixtures.go | 8 ++++++++ .../v1/resourceproviders/testing/requests_test.go | 10 ++++++++++ openstack/placement/v1/resourceproviders/urls.go | 4 ++++ 8 files changed, 64 insertions(+) diff --git a/acceptance/openstack/placement/v1/placement.go b/acceptance/openstack/placement/v1/placement.go index 079fe6c3f4..3ae96328b6 100644 --- a/acceptance/openstack/placement/v1/placement.go +++ b/acceptance/openstack/placement/v1/placement.go @@ -54,3 +54,17 @@ func CreateResourceProviderWithParent(t *testing.T, client *gophercloud.ServiceC return resourceProvider, nil } + +// DeleteResourceProvider will delete a resource provider with a specified ID. +// A fatal error will occur if the delete was not successful. This works best when +// used as a deferred function. +func DeleteResourceProvider(t *testing.T, client *gophercloud.ServiceClient, resourceProviderID string) { + t.Logf("Attempting to delete resourceProvider: %s", resourceProviderID) + + err := resourceproviders.Delete(client, resourceProviderID).ExtractErr() + if err != nil { + t.Fatalf("Unable to delete resourceProvider %s: %v", resourceProviderID, err) + } + + t.Logf("Deleted resourceProvider: %s.", resourceProviderID) +} diff --git a/acceptance/openstack/placement/v1/resourceproviders_test.go b/acceptance/openstack/placement/v1/resourceproviders_test.go index 0a946f4572..95c627dff8 100644 --- a/acceptance/openstack/placement/v1/resourceproviders_test.go +++ b/acceptance/openstack/placement/v1/resourceproviders_test.go @@ -34,9 +34,11 @@ func TestResourceProviderCreate(t *testing.T) { resourceProvider, err := CreateResourceProvider(t, client) th.AssertNoErr(t, err) + defer DeleteResourceProvider(t, client, resourceProvider.UUID) resourceProvider, err = CreateResourceProviderWithParent(t, client, resourceProvider.UUID) th.AssertNoErr(t, err) + defer DeleteResourceProvider(t, client, resourceProvider.UUID) } func TestResourceProviderUsages(t *testing.T) { @@ -50,6 +52,7 @@ func TestResourceProviderUsages(t *testing.T) { // first create new resource provider resourceProvider, err := CreateResourceProvider(t, client) th.AssertNoErr(t, err) + defer DeleteResourceProvider(t, client, resourceProvider.UUID) // now get the usages for the newly created resource provider usage, err := resourceproviders.GetUsages(client, resourceProvider.UUID).Extract() @@ -67,6 +70,7 @@ func TestResourceProviderInventories(t *testing.T) { // first create new resource provider resourceProvider, err := CreateResourceProvider(t, client) th.AssertNoErr(t, err) + defer DeleteResourceProvider(t, client, resourceProvider.UUID) // now get the inventories for the newly created resource provider usage, err := resourceproviders.GetInventories(client, resourceProvider.UUID).Extract() @@ -84,6 +88,7 @@ func TestResourceProviderTraits(t *testing.T) { // first create new resource provider resourceProvider, err := CreateResourceProvider(t, client) th.AssertNoErr(t, err) + defer DeleteResourceProvider(t, client, resourceProvider.UUID) // now get the traits for the newly created resource provider usage, err := resourceproviders.GetTraits(client, resourceProvider.UUID).Extract() @@ -101,6 +106,7 @@ func TestResourceProviderAllocations(t *testing.T) { // first create new resource provider resourceProvider, err := CreateResourceProvider(t, client) th.AssertNoErr(t, err) + defer DeleteResourceProvider(t, client, resourceProvider.UUID) // now get the allocations for the newly created resource provider usage, err := resourceproviders.GetAllocations(client, resourceProvider.UUID).Extract() diff --git a/openstack/placement/v1/resourceproviders/doc.go b/openstack/placement/v1/resourceproviders/doc.go index 236ee52b22..b19bfda5d3 100644 --- a/openstack/placement/v1/resourceproviders/doc.go +++ b/openstack/placement/v1/resourceproviders/doc.go @@ -30,6 +30,15 @@ Example to create resource providers panic(err) } +Example to Delete a resource provider + + resourceProviderID := "b99b3ab4-3aa6-4fba-b827-69b88b9c544a" + err := resourceproviders.Delete(placementClient, resourceProviderID).ExtractErr() + if err != nil { + panic(err) + } + + Example to get resource providers usages rp, err := resourceproviders.GetUsages(placementClient, resourceProviderID).Extract() diff --git a/openstack/placement/v1/resourceproviders/requests.go b/openstack/placement/v1/resourceproviders/requests.go index a8b54e3972..23a9dff589 100644 --- a/openstack/placement/v1/resourceproviders/requests.go +++ b/openstack/placement/v1/resourceproviders/requests.go @@ -99,6 +99,13 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create return } +// Delete accepts a unique ID and deletes the resource provider associated with it. +func Delete(c *gophercloud.ServiceClient, resourceProviderID string) (r DeleteResult) { + resp, err := c.Delete(deleteURL(c, resourceProviderID), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + func GetUsages(client *gophercloud.ServiceClient, resourceProviderID string) (r GetUsagesResult) { resp, err := client.Get(getResourceProviderUsagesURL(client, resourceProviderID), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/openstack/placement/v1/resourceproviders/results.go b/openstack/placement/v1/resourceproviders/results.go index 582cb86af7..8f70b8fd55 100644 --- a/openstack/placement/v1/resourceproviders/results.go +++ b/openstack/placement/v1/resourceproviders/results.go @@ -85,6 +85,12 @@ type CreateResult struct { resourceProviderResult } +// DeleteResult represents the result of a delete operation. Call its +// ExtractErr method to determine if the request succeeded or failed. +type DeleteResult struct { + gophercloud.ErrResult +} + // ResourceProvidersPage contains a single page of all resource providers from a List call. type ResourceProvidersPage struct { pagination.SinglePageBase diff --git a/openstack/placement/v1/resourceproviders/testing/fixtures.go b/openstack/placement/v1/resourceproviders/testing/fixtures.go index 29bf4c68fe..1909c4f49e 100644 --- a/openstack/placement/v1/resourceproviders/testing/fixtures.go +++ b/openstack/placement/v1/resourceproviders/testing/fixtures.go @@ -270,6 +270,14 @@ func HandleResourceProviderCreate(t *testing.T) { }) } +func HandleResourceProviderDelete(t *testing.T) { + th.Mux.HandleFunc("/resource_providers/b99b3ab4-3aa6-4fba-b827-69b88b9c544a", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "DELETE") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + w.WriteHeader(http.StatusNoContent) + }) +} + func HandleResourceProviderGetUsages(t *testing.T) { usageTestUrl := fmt.Sprintf("/resource_providers/%s/usages", ResourceProviderTestID) diff --git a/openstack/placement/v1/resourceproviders/testing/requests_test.go b/openstack/placement/v1/resourceproviders/testing/requests_test.go index 6eba22a560..d6a85ff835 100644 --- a/openstack/placement/v1/resourceproviders/testing/requests_test.go +++ b/openstack/placement/v1/resourceproviders/testing/requests_test.go @@ -57,6 +57,16 @@ func TestCreateResourceProvider(t *testing.T) { th.AssertDeepEquals(t, &expected, actual) } +func TestDeleteResourceProvider(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + HandleResourceProviderDelete(t) + + res := resourceproviders.Delete(fake.ServiceClient(), "b99b3ab4-3aa6-4fba-b827-69b88b9c544a") + th.AssertNoErr(t, res.Err) +} + func TestGetResourceProvidersUsages(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() diff --git a/openstack/placement/v1/resourceproviders/urls.go b/openstack/placement/v1/resourceproviders/urls.go index 6cc2a57c96..ed02e1c56c 100644 --- a/openstack/placement/v1/resourceproviders/urls.go +++ b/openstack/placement/v1/resourceproviders/urls.go @@ -10,6 +10,10 @@ func resourceProvidersListURL(client *gophercloud.ServiceClient) string { return client.ServiceURL(apiName) } +func deleteURL(client *gophercloud.ServiceClient, resourceProviderID string) string { + return client.ServiceURL(apiName, resourceProviderID) +} + func getResourceProviderUsagesURL(client *gophercloud.ServiceClient, resourceProviderID string) string { return client.ServiceURL(apiName, resourceProviderID, "usages") } From 60fab0fcc6696d63a6c1fe1e8d17ccd636f18ae3 Mon Sep 17 00:00:00 2001 From: nikParasyr <nik.parasyr@protonmail.com> Date: Wed, 23 Feb 2022 15:43:16 +0100 Subject: [PATCH 063/360] Add Get to placement resourceproviders [Docs](https://docs.openstack.org/api-ref/placement/?expanded=show-resource-provider-detail) Relates to: #526 --- .../placement/v1/resourceproviders_test.go | 9 +++++++-- openstack/placement/v1/resourceproviders/doc.go | 7 +++++++ .../placement/v1/resourceproviders/requests.go | 7 +++++++ .../placement/v1/resourceproviders/results.go | 6 ++++++ .../v1/resourceproviders/testing/fixtures.go | 12 ++++++++++++ .../v1/resourceproviders/testing/requests_test.go | 14 ++++++++++++++ openstack/placement/v1/resourceproviders/urls.go | 4 ++++ 7 files changed, 57 insertions(+), 2 deletions(-) diff --git a/acceptance/openstack/placement/v1/resourceproviders_test.go b/acceptance/openstack/placement/v1/resourceproviders_test.go index 95c627dff8..0d375815da 100644 --- a/acceptance/openstack/placement/v1/resourceproviders_test.go +++ b/acceptance/openstack/placement/v1/resourceproviders_test.go @@ -36,9 +36,14 @@ func TestResourceProviderCreate(t *testing.T) { th.AssertNoErr(t, err) defer DeleteResourceProvider(t, client, resourceProvider.UUID) - resourceProvider, err = CreateResourceProviderWithParent(t, client, resourceProvider.UUID) + resourceProvider2, err := CreateResourceProviderWithParent(t, client, resourceProvider.UUID) th.AssertNoErr(t, err) - defer DeleteResourceProvider(t, client, resourceProvider.UUID) + defer DeleteResourceProvider(t, client, resourceProvider2.UUID) + + resourceProviderGet, err := resourceproviders.Get(client, resourceProvider2.UUID).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, resourceProvider2.Name, resourceProviderGet.Name) + } func TestResourceProviderUsages(t *testing.T) { diff --git a/openstack/placement/v1/resourceproviders/doc.go b/openstack/placement/v1/resourceproviders/doc.go index b19bfda5d3..a90e2d09d0 100644 --- a/openstack/placement/v1/resourceproviders/doc.go +++ b/openstack/placement/v1/resourceproviders/doc.go @@ -38,6 +38,13 @@ Example to Delete a resource provider panic(err) } +Example to Get a resource provider + + resourceProviderID := "b99b3ab4-3aa6-4fba-b827-69b88b9c544a" + resourceProvider, err := resourceproviders.Get(placementClient, resourceProviderID).Extract() + if err != nil { + panic(err) + } Example to get resource providers usages diff --git a/openstack/placement/v1/resourceproviders/requests.go b/openstack/placement/v1/resourceproviders/requests.go index 23a9dff589..e9db99feb6 100644 --- a/openstack/placement/v1/resourceproviders/requests.go +++ b/openstack/placement/v1/resourceproviders/requests.go @@ -106,6 +106,13 @@ func Delete(c *gophercloud.ServiceClient, resourceProviderID string) (r DeleteRe return } +// Get retrieves a specific resource provider based on its unique ID. +func Get(c *gophercloud.ServiceClient, resourceProviderID string) (r GetResult) { + resp, err := c.Get(getURL(c, resourceProviderID), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + func GetUsages(client *gophercloud.ServiceClient, resourceProviderID string) (r GetUsagesResult) { resp, err := client.Get(getResourceProviderUsagesURL(client, resourceProviderID), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/openstack/placement/v1/resourceproviders/results.go b/openstack/placement/v1/resourceproviders/results.go index 8f70b8fd55..c33ed73f22 100644 --- a/openstack/placement/v1/resourceproviders/results.go +++ b/openstack/placement/v1/resourceproviders/results.go @@ -91,6 +91,12 @@ type DeleteResult struct { gophercloud.ErrResult } +// GetResult represents the result of a create operation. Call its Extract +// method to interpret it as a ResourceProvider. +type GetResult struct { + resourceProviderResult +} + // ResourceProvidersPage contains a single page of all resource providers from a List call. type ResourceProvidersPage struct { pagination.SinglePageBase diff --git a/openstack/placement/v1/resourceproviders/testing/fixtures.go b/openstack/placement/v1/resourceproviders/testing/fixtures.go index 1909c4f49e..8370ec29b2 100644 --- a/openstack/placement/v1/resourceproviders/testing/fixtures.go +++ b/openstack/placement/v1/resourceproviders/testing/fixtures.go @@ -270,6 +270,18 @@ func HandleResourceProviderCreate(t *testing.T) { }) } +func HandleResourceProviderGet(t *testing.T) { + th.Mux.HandleFunc("/resource_providers/99c09379-6e52-4ef8-9a95-b9ce6f68452e", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, ResourceProviderCreateBody) + }) +} + func HandleResourceProviderDelete(t *testing.T) { th.Mux.HandleFunc("/resource_providers/b99b3ab4-3aa6-4fba-b827-69b88b9c544a", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") diff --git a/openstack/placement/v1/resourceproviders/testing/requests_test.go b/openstack/placement/v1/resourceproviders/testing/requests_test.go index d6a85ff835..b8004322ac 100644 --- a/openstack/placement/v1/resourceproviders/testing/requests_test.go +++ b/openstack/placement/v1/resourceproviders/testing/requests_test.go @@ -57,6 +57,20 @@ func TestCreateResourceProvider(t *testing.T) { th.AssertDeepEquals(t, &expected, actual) } +func TestGetResourceProvider(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + HandleResourceProviderGet(t) + + expected := ExpectedResourceProvider1 + + actual, err := resourceproviders.Get(fake.ServiceClient(), ExpectedResourceProvider1.UUID).Extract() + th.AssertNoErr(t, err) + + th.AssertDeepEquals(t, &expected, actual) +} + func TestDeleteResourceProvider(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() diff --git a/openstack/placement/v1/resourceproviders/urls.go b/openstack/placement/v1/resourceproviders/urls.go index ed02e1c56c..3cd949be59 100644 --- a/openstack/placement/v1/resourceproviders/urls.go +++ b/openstack/placement/v1/resourceproviders/urls.go @@ -14,6 +14,10 @@ func deleteURL(client *gophercloud.ServiceClient, resourceProviderID string) str return client.ServiceURL(apiName, resourceProviderID) } +func getURL(client *gophercloud.ServiceClient, resourceProviderID string) string { + return client.ServiceURL(apiName, resourceProviderID) +} + func getResourceProviderUsagesURL(client *gophercloud.ServiceClient, resourceProviderID string) string { return client.ServiceURL(apiName, resourceProviderID, "usages") } From 5ed03611fb6abc85c9666f279bd75c1ff6872437 Mon Sep 17 00:00:00 2001 From: nikParasyr <nik.parasyr@protonmail.com> Date: Wed, 23 Feb 2022 17:27:09 +0100 Subject: [PATCH 064/360] Add Update to placement resourceproviders [Docs](https://docs.openstack.org/api-ref/placement/?expanded=update-resource-provider-detail) Relates to: #526 --- .../placement/v1/resourceproviders_test.go | 17 ++++++++- .../placement/v1/resourceproviders/doc.go | 15 ++++++++ .../v1/resourceproviders/requests.go | 37 ++++++++++++++++++ .../placement/v1/resourceproviders/results.go | 6 +++ .../v1/resourceproviders/testing/fixtures.go | 38 +++++++++++++++++++ .../testing/requests_test.go | 20 ++++++++++ .../placement/v1/resourceproviders/urls.go | 4 ++ 7 files changed, 135 insertions(+), 2 deletions(-) diff --git a/acceptance/openstack/placement/v1/resourceproviders_test.go b/acceptance/openstack/placement/v1/resourceproviders_test.go index 0d375815da..ee915ea035 100644 --- a/acceptance/openstack/placement/v1/resourceproviders_test.go +++ b/acceptance/openstack/placement/v1/resourceproviders_test.go @@ -26,7 +26,12 @@ func TestResourceProviderList(t *testing.T) { } } -func TestResourceProviderCreate(t *testing.T) { +func TestResourceProvider(t *testing.T) { + clients.SkipRelease(t, "stable/mitaka") + clients.SkipRelease(t, "stable/newton") + clients.SkipRelease(t, "stable/ocata") + clients.SkipRelease(t, "stable/pike") + clients.SkipRelease(t, "stable/queens") clients.RequireAdmin(t) client, err := clients.NewPlacementV1Client() @@ -40,9 +45,17 @@ func TestResourceProviderCreate(t *testing.T) { th.AssertNoErr(t, err) defer DeleteResourceProvider(t, client, resourceProvider2.UUID) + newName := tools.RandomString("TESTACC-", 8) + updateOpts := resourceproviders.UpdateOpts{ + Name: &newName, + } + resourceProviderUpdate, err := resourceproviders.Update(client, resourceProvider2.UUID, updateOpts).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, newName, resourceProviderUpdate.Name) + resourceProviderGet, err := resourceproviders.Get(client, resourceProvider2.UUID).Extract() th.AssertNoErr(t, err) - th.AssertEquals(t, resourceProvider2.Name, resourceProviderGet.Name) + th.AssertEquals(t, newName, resourceProviderGet.Name) } diff --git a/openstack/placement/v1/resourceproviders/doc.go b/openstack/placement/v1/resourceproviders/doc.go index a90e2d09d0..bb26372812 100644 --- a/openstack/placement/v1/resourceproviders/doc.go +++ b/openstack/placement/v1/resourceproviders/doc.go @@ -46,6 +46,21 @@ Example to Get a resource provider panic(err) } +Example to Update a resource provider + + resourceProviderID := "b99b3ab4-3aa6-4fba-b827-69b88b9c544a" + + updateOpts := resourceproviders.UpdateOpts{ + Name: "new-rp", + ParentProvider: "c7f50b40-6f32-4d7a-9f32-9384057be83b" + } + + placementClient.Microversion = "1.37" + resourceProvider, err := resourceproviders.Update(placementClient, resourceProviderID).Extract() + if err != nil { + panic(err) + } + Example to get resource providers usages rp, err := resourceproviders.GetUsages(placementClient, resourceProviderID).Extract() diff --git a/openstack/placement/v1/resourceproviders/requests.go b/openstack/placement/v1/resourceproviders/requests.go index e9db99feb6..6c638cd58b 100644 --- a/openstack/placement/v1/resourceproviders/requests.go +++ b/openstack/placement/v1/resourceproviders/requests.go @@ -113,6 +113,43 @@ func Get(c *gophercloud.ServiceClient, resourceProviderID string) (r GetResult) return } +// UpdateOptsBuilder allows extensions to add additional parameters to the +// Update request. +type UpdateOptsBuilder interface { + ToResourceProviderUpdateMap() (map[string]interface{}, error) +} + +// UpdateOpts represents options used to update a resource provider. +type UpdateOpts struct { + Name *string `json:"name,omitempty"` + // Available in version >= 1.37. It can be set to any existing provider UUID + // except to providers that would cause a loop. Also it can be set to null + // to transform the provider to a new root provider. This operation needs to + // be used carefully. Moving providers can mean that the original rules used + // to create the existing resource allocations may be invalidated by that move. + ParentProviderUUID *string `json:"parent_provider_uuid,omitempty"` +} + +// ToResourceProviderUpdateMap constructs a request body from UpdateOpts. +func (opts UpdateOpts) ToResourceProviderUpdateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "") +} + +// Update makes a request against the API to create a resource provider +func Update(client *gophercloud.ServiceClient, resourceProviderID string, opts UpdateOptsBuilder) (r UpdateResult) { + b, err := opts.ToResourceProviderUpdateMap() + if err != nil { + r.Err = err + return + } + + resp, err := client.Put(updateURL(client, resourceProviderID), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + func GetUsages(client *gophercloud.ServiceClient, resourceProviderID string) (r GetUsagesResult) { resp, err := client.Get(getResourceProviderUsagesURL(client, resourceProviderID), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/openstack/placement/v1/resourceproviders/results.go b/openstack/placement/v1/resourceproviders/results.go index c33ed73f22..cafff8b0ac 100644 --- a/openstack/placement/v1/resourceproviders/results.go +++ b/openstack/placement/v1/resourceproviders/results.go @@ -97,6 +97,12 @@ type GetResult struct { resourceProviderResult } +// UpdateResult represents the result of a update operation. Call its Extract +// method to interpret it as a ResourceProvider. +type UpdateResult struct { + resourceProviderResult +} + // ResourceProvidersPage contains a single page of all resource providers from a List call. type ResourceProvidersPage struct { pagination.SinglePageBase diff --git a/openstack/placement/v1/resourceproviders/testing/fixtures.go b/openstack/placement/v1/resourceproviders/testing/fixtures.go index 8370ec29b2..cdb8404c3a 100644 --- a/openstack/placement/v1/resourceproviders/testing/fixtures.go +++ b/openstack/placement/v1/resourceproviders/testing/fixtures.go @@ -62,6 +62,29 @@ const ResourceProviderCreateBody = ` } ` +const ResourceProviderUpdateResponse = ` +{ + "generation": 1, + "uuid": "4e8e5957-649f-477b-9e5b-f1f75b21c03c", + "links": [ + { + "href": "/resource_providers/4e8e5957-649f-477b-9e5b-f1f75b21c03c", + "rel": "self" + } + ], + "name": "new_name", + "parent_provider_uuid": "b99b3ab4-3aa6-4fba-b827-69b88b9c544a", + "root_provider_uuid": "542df8ed-9be2-49b9-b4db-6d3183ff8ec8" +} +` + +const ResourceProviderUpdateRequest = ` +{ + "name": "new_name", + "parent_provider_uuid": "b99b3ab4-3aa6-4fba-b827-69b88b9c544a" +} +` + const UsagesBody = ` { "resource_provider_generation": 1, @@ -290,6 +313,21 @@ func HandleResourceProviderDelete(t *testing.T) { }) } +func HandleResourceProviderUpdate(t *testing.T) { + th.Mux.HandleFunc("/resource_providers/4e8e5957-649f-477b-9e5b-f1f75b21c03c", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PUT") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestHeader(t, r, "Content-Type", "application/json") + th.TestHeader(t, r, "Accept", "application/json") + th.TestJSONRequest(t, r, ResourceProviderUpdateRequest) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, ResourceProviderUpdateResponse) + }) +} + func HandleResourceProviderGetUsages(t *testing.T) { usageTestUrl := fmt.Sprintf("/resource_providers/%s/usages", ResourceProviderTestID) diff --git a/openstack/placement/v1/resourceproviders/testing/requests_test.go b/openstack/placement/v1/resourceproviders/testing/requests_test.go index b8004322ac..741aeee934 100644 --- a/openstack/placement/v1/resourceproviders/testing/requests_test.go +++ b/openstack/placement/v1/resourceproviders/testing/requests_test.go @@ -81,6 +81,26 @@ func TestDeleteResourceProvider(t *testing.T) { th.AssertNoErr(t, res.Err) } +func TestUpdate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + HandleResourceProviderUpdate(t) + + name := "new_name" + parentProviderUUID := "b99b3ab4-3aa6-4fba-b827-69b88b9c544a" + + options := resourceproviders.UpdateOpts{ + Name: &name, + ParentProviderUUID: &parentProviderUUID, + } + rp, err := resourceproviders.Update(fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c", options).Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, rp.Name, name) + th.AssertEquals(t, rp.ParentProviderUUID, parentProviderUUID) +} + func TestGetResourceProvidersUsages(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() diff --git a/openstack/placement/v1/resourceproviders/urls.go b/openstack/placement/v1/resourceproviders/urls.go index 3cd949be59..5bcd53e80e 100644 --- a/openstack/placement/v1/resourceproviders/urls.go +++ b/openstack/placement/v1/resourceproviders/urls.go @@ -18,6 +18,10 @@ func getURL(client *gophercloud.ServiceClient, resourceProviderID string) string return client.ServiceURL(apiName, resourceProviderID) } +func updateURL(client *gophercloud.ServiceClient, resourceProviderID string) string { + return client.ServiceURL(apiName, resourceProviderID) +} + func getResourceProviderUsagesURL(client *gophercloud.ServiceClient, resourceProviderID string) string { return client.ServiceURL(apiName, resourceProviderID, "usages") } From 25ccd6aab1d3b13b5de54df6c6a2c8071e886e94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= <m.andre@redhat.com> Date: Fri, 18 Mar 2022 15:39:11 +0100 Subject: [PATCH 065/360] [CI-v2] Add clustering job with Github Action - Deploy Senlin with Devstack - Run clustering acceptance tests - Logs failures if any --- .github/workflows/functional-clustering.yaml | 60 ++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 .github/workflows/functional-clustering.yaml diff --git a/.github/workflows/functional-clustering.yaml b/.github/workflows/functional-clustering.yaml new file mode 100644 index 0000000000..71f6f786d4 --- /dev/null +++ b/.github/workflows/functional-clustering.yaml @@ -0,0 +1,60 @@ +name: functional-clustering +on: + pull_request: + paths: + - '**clustering**' + schedule: + - cron: '0 0 * * *' +jobs: + functional-clustering: + strategy: + fail-fast: false + matrix: + name: ["master"] + openstack_version: ["master"] + ubuntu_version: ["20.04"] + include: + - name: "xena" + openstack_version: "stable/xena" + ubuntu_version: "20.04" + - name: "wallaby" + openstack_version: "stable/wallaby" + ubuntu_version: "20.04" + - name: "victoria" + openstack_version: "stable/victoria" + ubuntu_version: "20.04" + - name: "ussuri" + openstack_version: "stable/ussuri" + ubuntu_version: "18.04" + - name: "train" + openstack_version: "stable/train" + ubuntu_version: "18.04" + runs-on: ubuntu-${{ matrix.ubuntu_version }} + name: Deploy OpenStack ${{ matrix.name }} with Senlin and run clustering acceptance tests + steps: + - name: Checkout Gophercloud + uses: actions/checkout@v2 + - name: Deploy devstack + uses: EmilienM/devstack-action@v0.6 + with: + branch: ${{ matrix.openstack_version }} + conf_overrides: | + enable_plugin senlin https://opendev.org/openstack/senlin ${{ matrix.openstack_version }} + - name: Checkout go + uses: actions/setup-go@v2 + with: + go-version: '^1.15' + - name: Run Gophercloud acceptance tests + run: ./script/acceptancetest + env: + DEVSTACK_PATH: ${{ github.workspace }}/devstack + ACCEPTANCE_TESTS_FILTER: "^.*clustering.*$" + - name: Generate logs on failure + run: ./script/collectlogs + if: failure() + - name: Upload logs artifacts on failure + if: failure() + uses: actions/upload-artifact@v2 + with: + name: functional-clustering-${{ matrix.name }} + path: /tmp/devstack-logs/* From 8b5661c81f466e35ff72feaaa571f028bc3645cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= <m.andre@redhat.com> Date: Fri, 18 Mar 2022 15:17:31 +0100 Subject: [PATCH 066/360] [CI-v2] Add orchestration job with Github Action * Deploy Heat with Devstack * Run placement acceptance tests * Logs failures if any --- .../workflows/functional-orchestration.yaml | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 .github/workflows/functional-orchestration.yaml diff --git a/.github/workflows/functional-orchestration.yaml b/.github/workflows/functional-orchestration.yaml new file mode 100644 index 0000000000..0ef49976b3 --- /dev/null +++ b/.github/workflows/functional-orchestration.yaml @@ -0,0 +1,61 @@ +name: functional-orchestration +on: + pull_request: + paths: + - '**orchestration**' + schedule: + - cron: '0 0 * * *' +jobs: + functional-orchestration: + strategy: + fail-fast: false + matrix: + name: ["master"] + openstack_version: ["master"] + ubuntu_version: ["20.04"] + include: + - name: "xena" + openstack_version: "stable/xena" + ubuntu_version: "20.04" + - name: "wallaby" + openstack_version: "stable/wallaby" + ubuntu_version: "20.04" + - name: "victoria" + openstack_version: "stable/victoria" + ubuntu_version: "20.04" + - name: "ussuri" + openstack_version: "stable/ussuri" + ubuntu_version: "18.04" + - name: "train" + openstack_version: "stable/train" + ubuntu_version: "18.04" + runs-on: ubuntu-${{ matrix.ubuntu_version }} + name: Deploy OpenStack ${{ matrix.name }} with Heat and run orchestration acceptance tests + steps: + - name: Checkout Gophercloud + uses: actions/checkout@v2 + - name: Deploy devstack + uses: EmilienM/devstack-action@v0.6 + with: + branch: ${{ matrix.openstack_version }} + conf_overrides: | + enable_plugin heat https://opendev.org/openstack/heat ${{ matrix.openstack_version }} + enabled_services: 'h-eng,h-api,h-api-cfn,h-api-cw' + - name: Checkout go + uses: actions/setup-go@v2 + with: + go-version: '^1.15' + - name: Run Gophercloud acceptance tests + run: ./script/acceptancetest + env: + DEVSTACK_PATH: ${{ github.workspace }}/devstack + ACCEPTANCE_TESTS_FILTER: ^.*orchestration.*$ + - name: Generate logs on failure + run: ./script/collectlogs + if: failure() + - name: Upload logs artifacts on failure + if: failure() + uses: actions/upload-artifact@v2 + with: + name: functional-orchestration-${{ matrix.name }} + path: /tmp/devstack-logs/* From bfc27428d59f6c4c3ddfe420cfacd9dc5617b470 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= <m.andre@redhat.com> Date: Fri, 18 Mar 2022 15:19:53 +0100 Subject: [PATCH 067/360] Stop skipping orchestration acceptance tests We expect these tests to pass with github actions, and if not they need to be fixed. --- acceptance/openstack/orchestration/v1/buildinfo_test.go | 2 -- acceptance/openstack/orchestration/v1/stackevents_test.go | 3 --- .../openstack/orchestration/v1/stackresources_test.go | 3 --- acceptance/openstack/orchestration/v1/stacks_test.go | 3 --- .../openstack/orchestration/v1/stacktemplates_test.go | 7 ------- 5 files changed, 18 deletions(-) diff --git a/acceptance/openstack/orchestration/v1/buildinfo_test.go b/acceptance/openstack/orchestration/v1/buildinfo_test.go index afbe2172ec..cec6376f80 100644 --- a/acceptance/openstack/orchestration/v1/buildinfo_test.go +++ b/acceptance/openstack/orchestration/v1/buildinfo_test.go @@ -12,8 +12,6 @@ import ( ) func TestBuildInfo(t *testing.T) { - clients.SkipRelease(t, "stable/mitaka") - client, err := clients.NewOrchestrationV1Client() th.AssertNoErr(t, err) diff --git a/acceptance/openstack/orchestration/v1/stackevents_test.go b/acceptance/openstack/orchestration/v1/stackevents_test.go index 65eb4cbf5e..150d51c9d8 100644 --- a/acceptance/openstack/orchestration/v1/stackevents_test.go +++ b/acceptance/openstack/orchestration/v1/stackevents_test.go @@ -12,9 +12,6 @@ import ( ) func TestStackEvents(t *testing.T) { - clients.SkipRelease(t, "stable/mitaka") - t.Skip("Currently failing in OpenLab") - client, err := clients.NewOrchestrationV1Client() th.AssertNoErr(t, err) diff --git a/acceptance/openstack/orchestration/v1/stackresources_test.go b/acceptance/openstack/orchestration/v1/stackresources_test.go index cbe5a44c8e..29c20e92e7 100644 --- a/acceptance/openstack/orchestration/v1/stackresources_test.go +++ b/acceptance/openstack/orchestration/v1/stackresources_test.go @@ -13,9 +13,6 @@ import ( ) func TestStackResources(t *testing.T) { - clients.SkipRelease(t, "stable/mitaka") - t.Skip("Currently failing in OpenLab") - client, err := clients.NewOrchestrationV1Client() th.AssertNoErr(t, err) diff --git a/acceptance/openstack/orchestration/v1/stacks_test.go b/acceptance/openstack/orchestration/v1/stacks_test.go index e9b0ee4321..f49818e4c3 100644 --- a/acceptance/openstack/orchestration/v1/stacks_test.go +++ b/acceptance/openstack/orchestration/v1/stacks_test.go @@ -13,9 +13,6 @@ import ( ) func TestStacksCRUD(t *testing.T) { - clients.SkipRelease(t, "stable/mitaka") - t.Skip("Currently failing in OpenLab") - client, err := clients.NewOrchestrationV1Client() th.AssertNoErr(t, err) diff --git a/acceptance/openstack/orchestration/v1/stacktemplates_test.go b/acceptance/openstack/orchestration/v1/stacktemplates_test.go index d1eed484f5..7513bcb43f 100644 --- a/acceptance/openstack/orchestration/v1/stacktemplates_test.go +++ b/acceptance/openstack/orchestration/v1/stacktemplates_test.go @@ -13,9 +13,6 @@ import ( ) func TestStackTemplatesCRUD(t *testing.T) { - clients.SkipRelease(t, "stable/mitaka") - t.Skip("Currently failing in OpenLab") - client, err := clients.NewOrchestrationV1Client() th.AssertNoErr(t, err) @@ -29,8 +26,6 @@ func TestStackTemplatesCRUD(t *testing.T) { } func TestStackTemplatesValidate(t *testing.T) { - clients.SkipRelease(t, "stable/mitaka") - client, err := clients.NewOrchestrationV1Client() th.AssertNoErr(t, err) @@ -44,8 +39,6 @@ func TestStackTemplatesValidate(t *testing.T) { } func TestStackTemplateWithFile(t *testing.T) { - clients.SkipRelease(t, "stable/mitaka") - t.Skip("Currently failing in OpenLab") client, err := clients.NewOrchestrationV1Client() th.AssertNoErr(t, err) From 1e22701d556a49b8087ce2092c12227edb818abc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= <m.andre@redhat.com> Date: Fri, 18 Mar 2022 17:17:08 +0100 Subject: [PATCH 068/360] Skip change_password operations in acceptance tests These require QEMU guest agent. --- acceptance/openstack/clustering/v1/clusters_test.go | 2 +- acceptance/openstack/clustering/v1/nodes_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/acceptance/openstack/clustering/v1/clusters_test.go b/acceptance/openstack/clustering/v1/clusters_test.go index 7c6f80666f..f5e72704c2 100644 --- a/acceptance/openstack/clustering/v1/clusters_test.go +++ b/acceptance/openstack/clustering/v1/clusters_test.go @@ -474,8 +474,8 @@ func TestClustersOps(t *testing.T) { // TODO: Commented out due to backend returns error, as of 2019-01-09 //{Operation: clusters.RebuildOperation}, // Error in set_admin_password in nova log //{Operation: clusters.EvacuateOperation, Params: clusters.OperationParams{"host": cluster.ID, "force": "True"}}, + //{Operation: clusters.ChangePasswordOperation, Params: clusters.OperationParams{"admin_pass": "test"}}, // QEMU guest agent is not enabled. {Operation: clusters.RebootOperation, Params: clusters.OperationParams{"type": "SOFT"}}, - {Operation: clusters.ChangePasswordOperation, Params: clusters.OperationParams{"admin_pass": "test"}}, {Operation: clusters.LockOperation}, {Operation: clusters.UnlockOperation}, {Operation: clusters.SuspendOperation}, diff --git a/acceptance/openstack/clustering/v1/nodes_test.go b/acceptance/openstack/clustering/v1/nodes_test.go index c41e34567a..50d178672d 100644 --- a/acceptance/openstack/clustering/v1/nodes_test.go +++ b/acceptance/openstack/clustering/v1/nodes_test.go @@ -94,8 +94,8 @@ func TestNodesOps(t *testing.T) { // TODO: Commented out due to backend returns error, as of 2018-12-14 //{Operation: nodes.RebuildOperation}, //{Operation: nodes.EvacuateOperation, Params: nodes.OperationParams{"EvacuateHost": node.ID, "EvacuateForce", "True"}}, + //{Operation: nodes.ChangePasswordOperation, Params: nodes.OperationParams{"admin_pass": "test"}}, // QEMU guest agent is not enabled. {Operation: nodes.RebootOperation, Params: nodes.OperationParams{"type": "SOFT"}}, - {Operation: nodes.ChangePasswordOperation, Params: nodes.OperationParams{"admin_pass": "test"}}, {Operation: nodes.LockOperation}, {Operation: nodes.UnlockOperation}, {Operation: nodes.SuspendOperation}, From fe346a7adc289b6edb7e9f70db63baeb92379e89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= <m.andre@redhat.com> Date: Fri, 18 Mar 2022 17:21:00 +0100 Subject: [PATCH 069/360] Enable zaqar in clustering job Messaging service is required for the receivers tests. --- .github/workflows/functional-clustering.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/functional-clustering.yaml b/.github/workflows/functional-clustering.yaml index 71f6f786d4..69b4a0dd59 100644 --- a/.github/workflows/functional-clustering.yaml +++ b/.github/workflows/functional-clustering.yaml @@ -40,6 +40,7 @@ jobs: branch: ${{ matrix.openstack_version }} conf_overrides: | enable_plugin senlin https://opendev.org/openstack/senlin ${{ matrix.openstack_version }} + enable_plugin zaqar https://opendev.org/openstack/zaqar ${{ matrix.openstack_version }} - name: Checkout go uses: actions/setup-go@v2 with: From 67be4c180c24ed4a9d269037cc3328eebd46c9f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= <m.andre@redhat.com> Date: Fri, 18 Mar 2022 17:25:11 +0100 Subject: [PATCH 070/360] [CI-v2] Add messaging job with Github Action - Deploy Zaqar with Devstack - Run messaging acceptance tests - Logs failures if any --- .github/workflows/functional-messaging.yaml | 60 +++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 .github/workflows/functional-messaging.yaml diff --git a/.github/workflows/functional-messaging.yaml b/.github/workflows/functional-messaging.yaml new file mode 100644 index 0000000000..f2bada69c5 --- /dev/null +++ b/.github/workflows/functional-messaging.yaml @@ -0,0 +1,60 @@ +name: functional-messaging +on: + pull_request: + paths: + - '**messaging**' + schedule: + - cron: '0 0 * * *' +jobs: + functional-messaging: + strategy: + fail-fast: false + matrix: + name: ["master"] + openstack_version: ["master"] + ubuntu_version: ["20.04"] + include: + - name: "xena" + openstack_version: "stable/xena" + ubuntu_version: "20.04" + - name: "wallaby" + openstack_version: "stable/wallaby" + ubuntu_version: "20.04" + - name: "victoria" + openstack_version: "stable/victoria" + ubuntu_version: "20.04" + - name: "ussuri" + openstack_version: "stable/ussuri" + ubuntu_version: "18.04" + - name: "train" + openstack_version: "stable/train" + ubuntu_version: "18.04" + runs-on: ubuntu-${{ matrix.ubuntu_version }} + name: Deploy OpenStack ${{ matrix.name }} with Zaqar and run messaging acceptance tests + steps: + - name: Checkout Gophercloud + uses: actions/checkout@v2 + - name: Deploy devstack + uses: EmilienM/devstack-action@v0.6 + with: + branch: ${{ matrix.openstack_version }} + conf_overrides: | + enable_plugin zaqar https://opendev.org/openstack/zaqar ${{ matrix.openstack_version }} + - name: Checkout go + uses: actions/setup-go@v2 + with: + go-version: '^1.15' + - name: Run Gophercloud acceptance tests + run: ./script/acceptancetest + env: + DEVSTACK_PATH: ${{ github.workspace }}/devstack + ACCEPTANCE_TESTS_FILTER: "^.*messaging.*$" + - name: Generate logs on failure + run: ./script/collectlogs + if: failure() + - name: Upload logs artifacts on failure + if: failure() + uses: actions/upload-artifact@v2 + with: + name: functional-messaging-${{ matrix.name }} + path: /tmp/devstack-logs/* From 9e3c98ba8077341e51bb3f038cd083af50a5f652 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= <martin.andre@gmail.com> Date: Sun, 20 Mar 2022 16:23:17 +0100 Subject: [PATCH 071/360] Remove default delay for message queue acceptance tests This causes messages list to not return any results during tests since we don't call it with the IncludeDelayed option. Rather than adding the IncludeDelayed option to message list calls, remove the default delay. --- acceptance/openstack/messaging/v2/messaging.go | 1 - 1 file changed, 1 deletion(-) diff --git a/acceptance/openstack/messaging/v2/messaging.go b/acceptance/openstack/messaging/v2/messaging.go index f782770b70..09f2641e09 100644 --- a/acceptance/openstack/messaging/v2/messaging.go +++ b/acceptance/openstack/messaging/v2/messaging.go @@ -21,7 +21,6 @@ func CreateQueue(t *testing.T, client *gophercloud.ServiceClient) (string, error QueueName: queueName, MaxMessagesPostSize: 262143, DefaultMessageTTL: 3700, - DefaultMessageDelay: 25, DeadLetterQueueMessagesTTL: 3500, MaxClaimCount: 10, Extra: map[string]interface{}{"description": "Test Queue for Gophercloud acceptance tests."}, From fcfa6acdc6c4856d93f31e8cad4b873b284e6f1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= <martin.andre@gmail.com> Date: Sun, 20 Mar 2022 16:27:03 +0100 Subject: [PATCH 072/360] Ensures TestListMessages get the desired number of messages The test would previously succeed when zaqar returned no messages. We're now checking we get the expected number of messages. --- acceptance/openstack/messaging/v2/message_test.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/acceptance/openstack/messaging/v2/message_test.go b/acceptance/openstack/messaging/v2/message_test.go index 7f6f09b9bd..b562023c61 100644 --- a/acceptance/openstack/messaging/v2/message_test.go +++ b/acceptance/openstack/messaging/v2/message_test.go @@ -10,6 +10,7 @@ import ( "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/messaging/v2/messages" "github.com/gophercloud/gophercloud/pagination" + th "github.com/gophercloud/gophercloud/testhelper" ) func TestListMessages(t *testing.T) { @@ -23,7 +24,10 @@ func TestListMessages(t *testing.T) { createdQueueName, err := CreateQueue(t, client) defer DeleteQueue(t, client, createdQueueName) - for i := 0; i < 3; i++ { + totalNumberOfMessages := 3 + currentNumberOfMessages := 0 + + for i := 0; i < totalNumberOfMessages; i++ { CreateMessage(t, client, createdQueueName) } @@ -41,11 +45,13 @@ func TestListMessages(t *testing.T) { } for _, message := range allMessages { + currentNumberOfMessages += 1 tools.PrintResource(t, message) } return true, nil }) + th.AssertEquals(t, totalNumberOfMessages, currentNumberOfMessages) } func TestCreateMessages(t *testing.T) { From 54a48626d04f9af41bfa5d34f5d70acad9ec95d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sandro=20J=C3=A4ckel?= <sandro.jaeckel@sap.com> Date: Fri, 11 Feb 2022 14:47:45 +0100 Subject: [PATCH 073/360] sharedfilesystems: add support for `/v2/services` api endpoint --- .../sharedfilesystems/v2/services/doc.go | 22 ++++++ .../sharedfilesystems/v2/services/requests.go | 49 +++++++++++++ .../sharedfilesystems/v2/services/results.go | 70 ++++++++++++++++++ .../v2/services/testing/fixtures.go | 71 +++++++++++++++++++ .../v2/services/testing/requests_test.go | 40 +++++++++++ .../sharedfilesystems/v2/services/urls.go | 7 ++ 6 files changed, 259 insertions(+) create mode 100644 openstack/sharedfilesystems/v2/services/doc.go create mode 100644 openstack/sharedfilesystems/v2/services/requests.go create mode 100644 openstack/sharedfilesystems/v2/services/results.go create mode 100644 openstack/sharedfilesystems/v2/services/testing/fixtures.go create mode 100644 openstack/sharedfilesystems/v2/services/testing/requests_test.go create mode 100644 openstack/sharedfilesystems/v2/services/urls.go diff --git a/openstack/sharedfilesystems/v2/services/doc.go b/openstack/sharedfilesystems/v2/services/doc.go new file mode 100644 index 0000000000..243b8e9b73 --- /dev/null +++ b/openstack/sharedfilesystems/v2/services/doc.go @@ -0,0 +1,22 @@ +/* +Package services returns information about the sharedfilesystems services in the +OpenStack cloud. + +Example of Retrieving list of all services + + allPages, err := services.List(sharedFileSystemV2, services.ListOpts{}).AllPages() + if err != nil { + panic(err) + } + + allServices, err := services.ExtractServices(allPages) + if err != nil { + panic(err) + } + + for _, service := range allServices { + fmt.Printf("%+v\n", service) + } +*/ + +package services diff --git a/openstack/sharedfilesystems/v2/services/requests.go b/openstack/sharedfilesystems/v2/services/requests.go new file mode 100644 index 0000000000..908e5a65c6 --- /dev/null +++ b/openstack/sharedfilesystems/v2/services/requests.go @@ -0,0 +1,49 @@ +package services + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// ListOptsBuilder allows extensions to add additional parameters to the List +// request. +type ListOptsBuilder interface { + ToServiceListQuery() (string, error) +} + +// ListOpts holds options for listing Services. +type ListOpts struct { + // The pool name for the back end. + ProjectID string `json:"project_id,omitempty"` + // The service host name. + Host string `json:"host"` + // The service binary name. Default is the base name of the executable. + Binary string `json:"binary"` + // The availability zone. + Zone string `json:"zone"` + // The current state of the service. A valid value is up or down. + State string `json:"state"` + // The service status, which is enabled or disabled. + Status string `json:"status"` +} + +// ToServiceListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToServiceListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// List makes a request against the API to list services. +func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := listURL(client) + if opts != nil { + query, err := opts.ToServiceListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return ServicePage{pagination.SinglePageBase(r)} + }) +} diff --git a/openstack/sharedfilesystems/v2/services/results.go b/openstack/sharedfilesystems/v2/services/results.go new file mode 100644 index 0000000000..ef498a5b51 --- /dev/null +++ b/openstack/sharedfilesystems/v2/services/results.go @@ -0,0 +1,70 @@ +package services + +import ( + "encoding/json" + "time" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// Service represents a Shared File System service in the OpenStack cloud. +type Service struct { + // The binary name of the service. + Binary string `json:"binary"` + + // The name of the host. + Host string `json:"host"` + + // The ID of the service. + ID int `json:"id"` + + // The state of the service. One of up or down. + State string `json:"state"` + + // The status of the service. One of available or unavailable. + Status string `json:"status"` + + // The date and time stamp when the extension was last updated. + UpdatedAt time.Time `json:"-"` + + // The availability zone name. + Zone string `json:"zone"` +} + +// UnmarshalJSON to override default +func (r *Service) UnmarshalJSON(b []byte) error { + type tmp Service + var s struct { + tmp + UpdatedAt gophercloud.JSONRFC3339MilliNoZ `json:"updated_at"` + } + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + *r = Service(s.tmp) + + r.UpdatedAt = time.Time(s.UpdatedAt) + + return nil +} + +// ServicePage represents a single page of all Services from a List request. +type ServicePage struct { + pagination.SinglePageBase +} + +// IsEmpty determines whether or not a page of Services contains any results. +func (page ServicePage) IsEmpty() (bool, error) { + services, err := ExtractServices(page) + return len(services) == 0, err +} + +func ExtractServices(r pagination.Page) ([]Service, error) { + var s struct { + Service []Service `json:"services"` + } + err := (r.(ServicePage)).ExtractInto(&s) + return s.Service, err +} diff --git a/openstack/sharedfilesystems/v2/services/testing/fixtures.go b/openstack/sharedfilesystems/v2/services/testing/fixtures.go new file mode 100644 index 0000000000..e36692d3f9 --- /dev/null +++ b/openstack/sharedfilesystems/v2/services/testing/fixtures.go @@ -0,0 +1,71 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + "time" + + "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/services" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +// ServiceListBody is sample response to the List call +const ServiceListBody = ` +{ + "services": [ + { + "status": "enabled", + "binary": "manila-share", + "zone": "manila", + "host": "manila2@generic1", + "updated_at": "2015-09-07T13:03:57.000000", + "state": "up", + "id": 1 + }, + { + "status": "enabled", + "binary": "manila-scheduler", + "zone": "manila", + "host": "manila2", + "updated_at": "2015-09-07T13:03:57.000000", + "state": "up", + "id": 2 + } + ] +} +` + +// First service from the ServiceListBody +var FirstFakeService = services.Service{ + Binary: "manila-share", + Host: "manila2@generic1", + ID: 1, + State: "up", + Status: "enabled", + UpdatedAt: time.Date(2015, 9, 7, 13, 3, 57, 0, time.UTC), + Zone: "manila", +} + +// Second service from the ServiceListBody +var SecondFakeService = services.Service{ + Binary: "manila-scheduler", + Host: "manila2", + ID: 2, + State: "up", + Status: "enabled", + UpdatedAt: time.Date(2015, 9, 7, 13, 3, 57, 0, time.UTC), + Zone: "manila", +} + +// HandleListSuccessfully configures the test server to respond to a List request. +func HandleListSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/services", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + w.Header().Add("Content-Type", "application/json") + fmt.Fprintf(w, ServiceListBody) + }) +} diff --git a/openstack/sharedfilesystems/v2/services/testing/requests_test.go b/openstack/sharedfilesystems/v2/services/testing/requests_test.go new file mode 100644 index 0000000000..1513379afa --- /dev/null +++ b/openstack/sharedfilesystems/v2/services/testing/requests_test.go @@ -0,0 +1,40 @@ +package testing + +import ( + "testing" + + "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/services" + "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestListServices(t *testing.T) { + testhelper.SetupHTTP() + defer testhelper.TeardownHTTP() + HandleListSuccessfully(t) + + pages := 0 + err := services.List(client.ServiceClient(), services.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + pages++ + + actual, err := services.ExtractServices(page) + if err != nil { + return false, err + } + + if len(actual) != 2 { + t.Fatalf("Expected 2 services, got %d", len(actual)) + } + testhelper.CheckDeepEquals(t, FirstFakeService, actual[0]) + testhelper.CheckDeepEquals(t, SecondFakeService, actual[1]) + + return true, nil + }) + + testhelper.AssertNoErr(t, err) + + if pages != 1 { + t.Errorf("Expected 1 page, saw %d", pages) + } +} diff --git a/openstack/sharedfilesystems/v2/services/urls.go b/openstack/sharedfilesystems/v2/services/urls.go new file mode 100644 index 0000000000..0e4acc11f5 --- /dev/null +++ b/openstack/sharedfilesystems/v2/services/urls.go @@ -0,0 +1,7 @@ +package services + +import "github.com/gophercloud/gophercloud" + +func listURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("services") +} From d7e7ccd27346b425c5b70d38cd72b2ce37b8c8b9 Mon Sep 17 00:00:00 2001 From: Emilien Macchi <emilien@redhat.com> Date: Tue, 5 Apr 2022 09:42:27 -0400 Subject: [PATCH 074/360] Stop using `go get` as required by latest golang See release notes: https://go.dev/doc/go1.18 ``` go get no longer builds or installs packages in module-aware mode. go get is now dedicated to adjusting dependencies in go.mod. Effectively, the -d flag is always enabled. To install the latest version of an executable outside the context of the current module, use go install example.com/cmd@latest. ``` Note: we fallback to `go get` if `go install` didn't work as expected, since we still run it on Go 1.14. --- .github/workflows/unit.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/unit.yml b/.github/workflows/unit.yml index c17e7eec5c..9094b7efa3 100644 --- a/.github/workflows/unit.yml +++ b/.github/workflows/unit.yml @@ -23,8 +23,8 @@ jobs: - name: Setup environment run: | - go get github.com/wadey/gocovmerge - go get golang.org/x/tools/cmd/goimports + go install github.com/wadey/gocovmerge@master || go get github.com/wadey/gocovmerge + go install golang.org/x/tools/cmd/goimports@latest || go get golang.org/x/tools/cmd/goimports - name: Run go vet run: | From e15f5ecfb5a48f8daf45fcd8f10a4de124e84044 Mon Sep 17 00:00:00 2001 From: Emilien Macchi <emilien@redhat.com> Date: Tue, 5 Apr 2022 09:23:34 -0400 Subject: [PATCH 075/360] Cleanup unused zuul jobs Now that we migrated to Github Actions, we don't need all these jobs. --- .github/workflows/unit.yml | 1 + .zuul.yaml | 242 ------------------ .../gophercloud-acceptance-test/run.yaml | 28 -- .zuul/playbooks/gophercloud-unittest/run.yaml | 17 -- 4 files changed, 1 insertion(+), 287 deletions(-) delete mode 100644 .zuul.yaml delete mode 100644 .zuul/playbooks/gophercloud-acceptance-test/run.yaml delete mode 100644 .zuul/playbooks/gophercloud-unittest/run.yaml diff --git a/.github/workflows/unit.yml b/.github/workflows/unit.yml index 9094b7efa3..b74a8f8fd3 100644 --- a/.github/workflows/unit.yml +++ b/.github/workflows/unit.yml @@ -34,6 +34,7 @@ jobs: run: | ./script/coverage ./script/format + ./script/unittest -v - uses: shogo82148/actions-goveralls@v1 with: diff --git a/.zuul.yaml b/.zuul.yaml deleted file mode 100644 index 99944fc100..0000000000 --- a/.zuul.yaml +++ /dev/null @@ -1,242 +0,0 @@ -- job: - name: gophercloud-unittest - parent: golang-test - description: | - Run gophercloud unit test - run: .zuul/playbooks/gophercloud-unittest/run.yaml - nodeset: ubuntu-xenial-ut - -- job: - name: gophercloud-acceptance-test-base - parent: golang-test - run: .zuul/playbooks/gophercloud-acceptance-test/run.yaml - description: | - Base job for all gophercloud acceptance tests - timeout: 18000 # 5 hours - abstract: true - nodeset: ubuntu-focal - irrelevant-files: - - ^.*\.md$ - - ^LICENSE$ - -- job: - name: gophercloud-acceptance-test-compute - parent: gophercloud-acceptance-test-base - description: | - Run gophercloud compute acceptance test on master branch. This runs when any file is patched - except if it's doc. - vars: - # the list of all available tests can generated by: - # find acceptance/openstack -name '*_test.go' -exec dirname {} \; | sort -n | uniq -c | awk '{print $2}' - acceptance_tests: - - acceptance/openstack - - acceptance/openstack/compute/v2 - - acceptance/openstack/container/v1 - - acceptance/openstack/identity/v2 - - acceptance/openstack/identity/v3 - - acceptance/openstack/keymanager/v1 - - acceptance/openstack/orchestration/v1 - - acceptance/openstack/placement/v1 - devstack_services: - - barbican - - heat - - zun - -- job: - name: gophercloud-acceptance-test-compute-ussuri - parent: gophercloud-acceptance-test-compute - nodeset: ubuntu-bionic - description: | - Run gophercloud compute acceptance test on ussuri branch - vars: - global_env: - OS_BRANCH: stable/ussuri - -- job: - name: gophercloud-acceptance-test-compute-train - parent: gophercloud-acceptance-test-compute - nodeset: ubuntu-xenial - description: | - Run gophercloud compute acceptance test on train branch - vars: - global_env: - OS_BRANCH: stable/train - -- job: - name: gophercloud-acceptance-test-compute-stein - parent: gophercloud-acceptance-test-compute - nodeset: ubuntu-xenial - description: | - Run gophercloud compute acceptance test on stein branch - vars: - global_env: - OS_BRANCH: stable/stein - -- job: - name: gophercloud-acceptance-test-compute-rocky - parent: gophercloud-acceptance-test-compute - nodeset: ubuntu-xenial - description: | - Run gophercloud compute acceptance test on rocky branch - vars: - global_env: - OS_BRANCH: stable/rocky - -- job: - name: gophercloud-acceptance-test-compute-queens - parent: gophercloud-acceptance-test-compute - description: | - Run gophercloud compute acceptance test on queens branch - nodeset: ubuntu-xenial - vars: - global_env: - OS_BRANCH: stable/queens - -# NOTE: A Pike-based devstack environment is currently -# not building correctly. This might be a temporary issue. -- job: - name: gophercloud-acceptance-test-compute-pike - parent: gophercloud-acceptance-test-compute - description: | - Run gophercloud compute acceptance test on pike branch - nodeset: ubuntu-xenial - vars: - global_env: - OS_BRANCH: stable/pike - -- job: - name: gophercloud-acceptance-test-compute-ocata - parent: gophercloud-acceptance-test-compute - description: | - Run gophercloud compute acceptance test on ocata branch - nodeset: ubuntu-xenial - vars: - global_env: - OS_BRANCH: stable/ocata - -# NOTE: A Newton-based devstack environment is currently -# not building correctly. This might be a temporary issue. -- job: - name: gophercloud-acceptance-test-compute-newton - parent: gophercloud-acceptance-test-compute - description: | - Run gophercloud compute acceptance test on newton branch - nodeset: ubuntu-xenial - vars: - global_env: - OS_BRANCH: stable/newton - -# The following jobs are maintained because they are parents -# for gophercloud v0.4.0 acceptance tests in theopenlab/openlab-zuul-jobs. -# It'll be removed once we finished the migration to Github actions. -- job: - name: gophercloud-acceptance-test-legacy - parent: gophercloud-acceptance-test-base - description: | - THIS JOB REMAINS FOR LEGACY. Will be removed soon to be replaced by its variants. - Run gophercloud acceptance test on the master branch. This runs when any file is patched - except if it's doc. - vars: - acceptance_tests: - - acceptance/openstack - - acceptance/openstack/container/v1 - - acceptance/openstack/orchestration/v1 - - acceptance/openstack/placement/v1 - devstack_services: - - manila - - zun - -- job: - name: gophercloud-acceptance-test-stein - parent: gophercloud-acceptance-test-legacy - nodeset: ubuntu-xenial - description: | - Run gophercloud acceptance test on stein branch - vars: - global_env: - OS_BRANCH: stable/stein - -- job: - name: gophercloud-acceptance-test-rocky - parent: gophercloud-acceptance-test-legacy - nodeset: ubuntu-xenial - description: | - Run gophercloud acceptance test on rocky branch - vars: - global_env: - OS_BRANCH: stable/rocky - -- job: - name: gophercloud-acceptance-test-queens - parent: gophercloud-acceptance-test-legacy - description: | - Run gophercloud acceptance test on queens branch - nodeset: ubuntu-xenial - vars: - global_env: - OS_BRANCH: stable/queens - -# NOTE: A Pike-based devstack environment is currently -# not building correctly. This might be a temporary issue. -- job: - name: gophercloud-acceptance-test-pike - parent: gophercloud-acceptance-test-legacy - description: | - Run gophercloud acceptance test on pike branch - nodeset: ubuntu-xenial - vars: - global_env: - OS_BRANCH: stable/pike - -- job: - name: gophercloud-acceptance-test-ocata - parent: gophercloud-acceptance-test-legacy - description: | - Run gophercloud acceptance test on ocata branch - nodeset: ubuntu-xenial - vars: - global_env: - OS_BRANCH: stable/ocata - -# NOTE: A Newton-based devstack environment is currently -# not building correctly. This might be a temporary issue. -- job: - name: gophercloud-acceptance-test-newton - parent: gophercloud-acceptance-test-legacy - description: | - Run gophercloud acceptance test on newton branch - nodeset: ubuntu-xenial - vars: - global_env: - OS_BRANCH: stable/newton - -- project: - name: gophercloud/gophercloud - check: - jobs: - - gophercloud-unittest - - gophercloud-acceptance-test-compute - recheck-newton: - jobs: - - gophercloud-acceptance-test-compute-newton - recheck-ocata: - jobs: - - gophercloud-acceptance-test-compute-ocata - recheck-pike: - jobs: - - gophercloud-acceptance-test-compute-pike - recheck-queens: - jobs: - - gophercloud-acceptance-test-compute-queens - recheck-rocky: - jobs: - - gophercloud-acceptance-test-compute-rocky - recheck-stein: - jobs: - - gophercloud-acceptance-test-compute-stein - recheck-train: - jobs: - - gophercloud-acceptance-test-compute-train - recheck-ussuri: - jobs: - - gophercloud-acceptance-test-compute-ussuri diff --git a/.zuul/playbooks/gophercloud-acceptance-test/run.yaml b/.zuul/playbooks/gophercloud-acceptance-test/run.yaml deleted file mode 100644 index 844e2a8c68..0000000000 --- a/.zuul/playbooks/gophercloud-acceptance-test/run.yaml +++ /dev/null @@ -1,28 +0,0 @@ -- hosts: all - become: yes - roles: - - role: config-golang - go_version: '1.15' - - clone-devstack-gate-to-workspace - - role: create-devstack-local-conf - enable_services: "{{ devstack_services | default(omit) }}" - - role: prefetch-amphora - when: prefetch_amphora|default(false) - - role: install-devstack - environment: - OVERRIDE_ENABLED_SERVICES: "{{ devstack_override_enabled_services | default('') }}" - PROJECTS: "{{ devstack_projects | default('') }}" - tasks: - - name: Run acceptance tests with gophercloud - shell: - cmd: | - set -e - set -o pipefail - set -x - echo $(export |grep OS_BRANCH) - export ACCEPTANCE_TESTS="{{ acceptance_tests|default('') }}" - go get ./... || true - ./script/acceptancetest -v 2>&1 | tee $TEST_RESULTS_TXT - executable: /bin/bash - chdir: '{{ zuul.project.src_dir }}' - environment: '{{ global_env }}' diff --git a/.zuul/playbooks/gophercloud-unittest/run.yaml b/.zuul/playbooks/gophercloud-unittest/run.yaml deleted file mode 100644 index 6303b3bed8..0000000000 --- a/.zuul/playbooks/gophercloud-unittest/run.yaml +++ /dev/null @@ -1,17 +0,0 @@ -- hosts: all - become: yes - roles: - - role: config-golang - go_version: '1.15' - tasks: - - name: Run unit tests with gophercloud - shell: - cmd: | - set -e - set -o pipefail - set -x - go get ./... || true - ./script/unittest -v 2>&1 | tee $TEST_RESULTS_TXT - executable: /bin/bash - chdir: '{{ zuul.project.src_dir }}' - environment: '{{ global_env }}' From cdf56ef49db2e00674ca4275d88142def2385a57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= <m.andre@redhat.com> Date: Fri, 8 Apr 2022 11:20:00 +0200 Subject: [PATCH 076/360] Fix greetings workflow on PRs The greetings workflow template for github action didn't have the permission to comment when the PR originated from a fork [1]. It would return a `Resource not accessible by integration` error as seen in [2]. We need to use the `pull_request_target` [3] trigger rather than `pull_request` to allow the workflow to execute in the context of the target repository. [1] https://docs.github.com/en/actions/security-guides/automatic-token-authentication#permissions-for-the-github_token [2] https://github.com/gophercloud/gophercloud/runs/5881926600?check_suite_focus=true [3] https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_target --- .github/workflows/greetings.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/greetings.yml b/.github/workflows/greetings.yml index 4203b030b9..7bd51ee5e8 100644 --- a/.github/workflows/greetings.yml +++ b/.github/workflows/greetings.yml @@ -1,6 +1,6 @@ name: Greetings -on: [pull_request, issues] +on: [pull_request_target, issues] jobs: greeting: From 7c294a0bce003ccab84cf91edd77ed6305030708 Mon Sep 17 00:00:00 2001 From: shhgs <shhgs@yahoo.com> Date: Sat, 23 Oct 2021 22:26:56 -0400 Subject: [PATCH 077/360] Neutron v2: BGP Peer list / get --- .../networking/v2/extensions/bgp/peers/doc.go | 32 ++++++++ .../v2/extensions/bgp/peers/requests.go | 21 +++++ .../v2/extensions/bgp/peers/results.go | 78 +++++++++++++++++++ .../v2/extensions/bgp/peers/testing/doc.go | 2 + .../extensions/bgp/peers/testing/fixture.go | 62 +++++++++++++++ .../bgp/peers/testing/requests_test.go | 60 ++++++++++++++ .../v2/extensions/bgp/peers/urls.go | 25 ++++++ 7 files changed, 280 insertions(+) create mode 100644 openstack/networking/v2/extensions/bgp/peers/doc.go create mode 100644 openstack/networking/v2/extensions/bgp/peers/requests.go create mode 100644 openstack/networking/v2/extensions/bgp/peers/results.go create mode 100644 openstack/networking/v2/extensions/bgp/peers/testing/doc.go create mode 100644 openstack/networking/v2/extensions/bgp/peers/testing/fixture.go create mode 100644 openstack/networking/v2/extensions/bgp/peers/testing/requests_test.go create mode 100644 openstack/networking/v2/extensions/bgp/peers/urls.go diff --git a/openstack/networking/v2/extensions/bgp/peers/doc.go b/openstack/networking/v2/extensions/bgp/peers/doc.go new file mode 100644 index 0000000000..9a6af137aa --- /dev/null +++ b/openstack/networking/v2/extensions/bgp/peers/doc.go @@ -0,0 +1,32 @@ +package peers + +/* +Package peers contains the functionality for working with Neutron bgp peers. + +1. List BGP Peers, a.k.a. GET /bgp-peers + +Example: + + pages, err := peers.List(c).AllPages() + if err != nil { + log.Panic(err) + } + allPeers, err := peers.ExtractBGPPeers(pages) + if err != nil { + log.Panic(err) + } + + for _, peer := range allPeers { + log.Printf("%+v", peer) + } + +2. Get BGP Peer, a.k.a. GET /bgp-peers/{id} + +Example: + p, err := peers.Get(c, id).Extract() + + if err != nil { + log.Panic(err) + } + log.Printf("%+v", *p) +*/ diff --git a/openstack/networking/v2/extensions/bgp/peers/requests.go b/openstack/networking/v2/extensions/bgp/peers/requests.go new file mode 100644 index 0000000000..e9a1c8c41c --- /dev/null +++ b/openstack/networking/v2/extensions/bgp/peers/requests.go @@ -0,0 +1,21 @@ +package peers + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// List the bgp peers +func List(c *gophercloud.ServiceClient) pagination.Pager { + url := listURL(c) + return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { + return BGPPeerPage{pagination.SinglePageBase(r)} + }) +} + +// Get retrieve the specific bgp peer by its uuid +func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := c.Get(getURL(c, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} diff --git a/openstack/networking/v2/extensions/bgp/peers/results.go b/openstack/networking/v2/extensions/bgp/peers/results.go new file mode 100644 index 0000000000..5574c5e410 --- /dev/null +++ b/openstack/networking/v2/extensions/bgp/peers/results.go @@ -0,0 +1,78 @@ +package peers + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +const jroot = "bgp_peer" + +type commonResult struct { + gophercloud.Result +} + +// Extract is a function that accepts a result and extracts a bgp peer resource. +func (r commonResult) Extract() (*BGPPeer, error) { + var s BGPPeer + err := r.ExtractInto(&s) + return &s, err +} + +func (r commonResult) ExtractInto(v interface{}) error { + return r.Result.ExtractIntoStructPtr(v, jroot) +} + +// BGP peer +type BGPPeer struct { + // AuthType of the BGP Speaker + AuthType string `json:"auth_type"` + + // UUID for the bgp peer + ID string `json:"id"` + + // Human-readable name for the bgp peer. Might not be unique. + Name string `json:"name"` + + // TenantID is the project owner of the bgp peer. + TenantID string `json:"tenant_id"` + + // The IP addr of the BGP Peer + PeerIP string `json:"peer_ip"` + + // ProjectID is the project owner of the bgp peer. + ProjectID string `json:"project_id"` + + // Remote Autonomous System + RemoteAS int `json:"remote_as"` +} + +// BGPPeerPage is the page returned by a pager when traversing over a +// collection of bgp peers. +type BGPPeerPage struct { + pagination.SinglePageBase +} + +// IsEmpty checks whether a BGPPage struct is empty. +func (r BGPPeerPage) IsEmpty() (bool, error) { + is, err := ExtractBGPPeers(r) + return len(is) == 0, err +} + +// ExtractBGPPeers accepts a Page struct, specifically a BGPPeerPage struct, +// and extracts the elements into a slice of BGPPeer structs. In other words, +// a generic collection is mapped into a relevant slice. +func ExtractBGPPeers(r pagination.Page) ([]BGPPeer, error) { + var s []BGPPeer + err := ExtractBGPPeersInto(r, &s) + return s, err +} + +func ExtractBGPPeersInto(r pagination.Page, v interface{}) error { + return r.(BGPPeerPage).Result.ExtractIntoSlicePtr(v, "bgp_peers") +} + +// GetResult represents the result of a get operation. Call its Extract +// method to interpret it as a BGPPeer. +type GetResult struct { + commonResult +} diff --git a/openstack/networking/v2/extensions/bgp/peers/testing/doc.go b/openstack/networking/v2/extensions/bgp/peers/testing/doc.go new file mode 100644 index 0000000000..e793248a66 --- /dev/null +++ b/openstack/networking/v2/extensions/bgp/peers/testing/doc.go @@ -0,0 +1,2 @@ +// Package testing fro bgp peers +package testing diff --git a/openstack/networking/v2/extensions/bgp/peers/testing/fixture.go b/openstack/networking/v2/extensions/bgp/peers/testing/fixture.go new file mode 100644 index 0000000000..8396e6ec55 --- /dev/null +++ b/openstack/networking/v2/extensions/bgp/peers/testing/fixture.go @@ -0,0 +1,62 @@ +package testing + +import "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/bgp/peers" + +const ListBGPPeersResult = ` +{ + "bgp_peers": [ + { + "auth_type": "none", + "remote_as": 4321, + "name": "testing-peer-1", + "tenant_id": "7fa3f96b-17ee-4d1b-8fbf-fe889bb1f1d0", + "peer_ip": "1.2.3.4", + "project_id": "7fa3f96b-17ee-4d1b-8fbf-fe889bb1f1d0", + "id": "afacc0e8-6b66-44e4-be53-a1ef16033ceb" + }, + { + "auth_type": "none", + "remote_as": 4321, + "name": "testing-peer-2", + "tenant_id": "7fa3f96b-17ee-4d1b-8fbf-fe889bb1f1d0", + "peer_ip": "5.6.7.8", + "project_id": "7fa3f96b-17ee-4d1b-8fbf-fe889bb1f1d0", + "id": "acd7c4a1-e243-4fe5-80f9-eba8f143ac1d" + } + ] +} +` + +var BGPPeer1 = peers.BGPPeer{ + ID: "afacc0e8-6b66-44e4-be53-a1ef16033ceb", + AuthType: "none", + Name: "testing-peer-1", + TenantID: "7fa3f96b-17ee-4d1b-8fbf-fe889bb1f1d0", + PeerIP: "1.2.3.4", + ProjectID: "7fa3f96b-17ee-4d1b-8fbf-fe889bb1f1d0", + RemoteAS: 4321, +} + +var BGPPeer2 = peers.BGPPeer{ + AuthType: "none", + ID: "acd7c4a1-e243-4fe5-80f9-eba8f143ac1d", + Name: "testing-peer-2", + TenantID: "7fa3f96b-17ee-4d1b-8fbf-fe889bb1f1d0", + PeerIP: "5.6.7.8", + ProjectID: "7fa3f96b-17ee-4d1b-8fbf-fe889bb1f1d0", + RemoteAS: 4321, +} + +const GetBGPPeerResult = ` +{ + "bgp_peer": { + "auth_type": "none", + "remote_as": 4321, + "name": "testing-peer-1", + "tenant_id": "7fa3f96b-17ee-4d1b-8fbf-fe889bb1f1d0", + "peer_ip": "1.2.3.4", + "project_id": "7fa3f96b-17ee-4d1b-8fbf-fe889bb1f1d0", + "id": "afacc0e8-6b66-44e4-be53-a1ef16033ceb" + } +} +` diff --git a/openstack/networking/v2/extensions/bgp/peers/testing/requests_test.go b/openstack/networking/v2/extensions/bgp/peers/testing/requests_test.go new file mode 100644 index 0000000000..980ecaaedb --- /dev/null +++ b/openstack/networking/v2/extensions/bgp/peers/testing/requests_test.go @@ -0,0 +1,60 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/bgp/peers" + "github.com/gophercloud/gophercloud/pagination" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestList(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/bgp-peers", + func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, ListBGPPeersResult) + }) + count := 0 + + peers.List(fake.ServiceClient()).EachPage( + func(page pagination.Page) (bool, error) { + count++ + actual, err := peers.ExtractBGPPeers(page) + + if err != nil { + t.Errorf("Failed to extract BGP Peers: %v", err) + return false, nil + } + expected := []peers.BGPPeer{BGPPeer1, BGPPeer2} + th.CheckDeepEquals(t, expected, actual) + return true, nil + }) +} + +func TestGet(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + bgpPeerID := "afacc0e8-6b66-44e4-be53-a1ef16033ceb" + th.Mux.HandleFunc("/v2.0/bgp-peers/"+bgpPeerID, func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, GetBGPPeerResult) + }) + + s, err := peers.Get(fake.ServiceClient(), bgpPeerID).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, *s, BGPPeer1) +} diff --git a/openstack/networking/v2/extensions/bgp/peers/urls.go b/openstack/networking/v2/extensions/bgp/peers/urls.go new file mode 100644 index 0000000000..43a288311e --- /dev/null +++ b/openstack/networking/v2/extensions/bgp/peers/urls.go @@ -0,0 +1,25 @@ +package peers + +import "github.com/gophercloud/gophercloud" + +const urlBase = "bgp-peers" + +// return /v2.0/bgp-peers/{bgp-peer-id} +func resourceURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL(urlBase, id) +} + +// return /v2.0/bgp-peers +func rootURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL(urlBase) +} + +// return /v2.0/bgp-peers/{bgp-peer-id} +func getURL(c *gophercloud.ServiceClient, id string) string { + return resourceURL(c, id) +} + +// return /v2.0/bgp-peers +func listURL(c *gophercloud.ServiceClient) string { + return rootURL(c) +} From b60db87b3f28d75d6c1554e2834b67ff869ec083 Mon Sep 17 00:00:00 2001 From: Emilien Macchi <emilien@redhat.com> Date: Tue, 5 Apr 2022 09:26:46 -0400 Subject: [PATCH 078/360] CI: add jobs for stable/yoga new release of OpenStack: Yoga --- .github/workflows/functional-baremetal.yaml | 3 +++ .github/workflows/functional-basic.yaml | 3 +++ .github/workflows/functional-blockstorage.yaml | 3 +++ .github/workflows/functional-clustering.yaml | 3 +++ .github/workflows/functional-compute.yaml | 3 +++ .github/workflows/functional-dns.yaml | 3 +++ .github/workflows/functional-identity.yaml | 3 +++ .github/workflows/functional-imageservice.yaml | 3 +++ .github/workflows/functional-keymanager.yaml | 3 +++ .github/workflows/functional-loadbalancer.yaml | 3 +++ .github/workflows/functional-messaging.yaml | 3 +++ .github/workflows/functional-networking.yaml | 4 ++++ .github/workflows/functional-objectstorage.yaml | 3 +++ .github/workflows/functional-orchestration.yaml | 3 +++ .github/workflows/functional-placement.yaml | 3 +++ .github/workflows/functional-sharedfilesystems.yaml | 3 +++ 16 files changed, 49 insertions(+) diff --git a/.github/workflows/functional-baremetal.yaml b/.github/workflows/functional-baremetal.yaml index 26efcddfd1..02b0c76c45 100644 --- a/.github/workflows/functional-baremetal.yaml +++ b/.github/workflows/functional-baremetal.yaml @@ -14,6 +14,9 @@ jobs: openstack_version: ["master"] ubuntu_version: ["20.04"] include: + - name: "yoga" + openstack_version: "stable/yoga" + ubuntu_version: "20.04" - name: "xena" openstack_version: "stable/xena" ubuntu_version: "20.04" diff --git a/.github/workflows/functional-basic.yaml b/.github/workflows/functional-basic.yaml index 4ae4292793..574e15f23a 100644 --- a/.github/workflows/functional-basic.yaml +++ b/.github/workflows/functional-basic.yaml @@ -17,6 +17,9 @@ jobs: openstack_version: ["master"] ubuntu_version: ["20.04"] include: + - name: "yoga" + openstack_version: "stable/yoga" + ubuntu_version: "20.04" - name: "xena" openstack_version: "stable/xena" ubuntu_version: "20.04" diff --git a/.github/workflows/functional-blockstorage.yaml b/.github/workflows/functional-blockstorage.yaml index f79c8d0d12..933dd433dc 100644 --- a/.github/workflows/functional-blockstorage.yaml +++ b/.github/workflows/functional-blockstorage.yaml @@ -14,6 +14,9 @@ jobs: openstack_version: ["master"] ubuntu_version: ["20.04"] include: + - name: "yoga" + openstack_version: "stable/yoga" + ubuntu_version: "20.04" - name: "xena" openstack_version: "stable/xena" ubuntu_version: "20.04" diff --git a/.github/workflows/functional-clustering.yaml b/.github/workflows/functional-clustering.yaml index 69b4a0dd59..766ca770b7 100644 --- a/.github/workflows/functional-clustering.yaml +++ b/.github/workflows/functional-clustering.yaml @@ -14,6 +14,9 @@ jobs: openstack_version: ["master"] ubuntu_version: ["20.04"] include: + - name: "yoga" + openstack_version: "stable/yoga" + ubuntu_version: "20.04" - name: "xena" openstack_version: "stable/xena" ubuntu_version: "20.04" diff --git a/.github/workflows/functional-compute.yaml b/.github/workflows/functional-compute.yaml index b002b82a68..a5bc4d317d 100644 --- a/.github/workflows/functional-compute.yaml +++ b/.github/workflows/functional-compute.yaml @@ -14,6 +14,9 @@ jobs: openstack_version: ["master"] ubuntu_version: ["20.04"] include: + - name: "yoga" + openstack_version: "stable/yoga" + ubuntu_version: "20.04" - name: "xena" openstack_version: "stable/xena" ubuntu_version: "20.04" diff --git a/.github/workflows/functional-dns.yaml b/.github/workflows/functional-dns.yaml index 8fd78bfe9d..13bc091978 100644 --- a/.github/workflows/functional-dns.yaml +++ b/.github/workflows/functional-dns.yaml @@ -15,6 +15,9 @@ jobs: openstack_version: ["master"] ubuntu_version: ["20.04"] include: + - name: "yoga" + openstack_version: "stable/yoga" + ubuntu_version: "20.04" - name: "xena" openstack_version: "stable/xena" ubuntu_version: "20.04" diff --git a/.github/workflows/functional-identity.yaml b/.github/workflows/functional-identity.yaml index 5e0f5d8535..6a73c87b71 100644 --- a/.github/workflows/functional-identity.yaml +++ b/.github/workflows/functional-identity.yaml @@ -14,6 +14,9 @@ jobs: openstack_version: ["master"] ubuntu_version: ["20.04"] include: + - name: "yoga" + openstack_version: "stable/yoga" + ubuntu_version: "20.04" - name: "xena" openstack_version: "stable/xena" ubuntu_version: "20.04" diff --git a/.github/workflows/functional-imageservice.yaml b/.github/workflows/functional-imageservice.yaml index dd24b7af55..2bacedcdf6 100644 --- a/.github/workflows/functional-imageservice.yaml +++ b/.github/workflows/functional-imageservice.yaml @@ -14,6 +14,9 @@ jobs: openstack_version: ["master"] ubuntu_version: ["20.04"] include: + - name: "yoga" + openstack_version: "stable/yoga" + ubuntu_version: "20.04" - name: "xena" openstack_version: "stable/xena" ubuntu_version: "20.04" diff --git a/.github/workflows/functional-keymanager.yaml b/.github/workflows/functional-keymanager.yaml index 200dae116d..20c4b79d26 100644 --- a/.github/workflows/functional-keymanager.yaml +++ b/.github/workflows/functional-keymanager.yaml @@ -14,6 +14,9 @@ jobs: openstack_version: ["master"] ubuntu_version: ["20.04"] include: + - name: "yoga" + openstack_version: "stable/yoga" + ubuntu_version: "20.04" - name: "xena" openstack_version: "stable/xena" ubuntu_version: "20.04" diff --git a/.github/workflows/functional-loadbalancer.yaml b/.github/workflows/functional-loadbalancer.yaml index a963793453..72479bd4fe 100644 --- a/.github/workflows/functional-loadbalancer.yaml +++ b/.github/workflows/functional-loadbalancer.yaml @@ -14,6 +14,9 @@ jobs: openstack_version: ["master"] ubuntu_version: ["20.04"] include: + - name: "yoga" + openstack_version: "stable/yoga" + ubuntu_version: "20.04" - name: "xena" openstack_version: "stable/xena" ubuntu_version: "20.04" diff --git a/.github/workflows/functional-messaging.yaml b/.github/workflows/functional-messaging.yaml index f2bada69c5..89c728eedc 100644 --- a/.github/workflows/functional-messaging.yaml +++ b/.github/workflows/functional-messaging.yaml @@ -14,6 +14,9 @@ jobs: openstack_version: ["master"] ubuntu_version: ["20.04"] include: + - name: "yoga" + openstack_version: "stable/yoga" + ubuntu_version: "20.04" - name: "xena" openstack_version: "stable/xena" ubuntu_version: "20.04" diff --git a/.github/workflows/functional-networking.yaml b/.github/workflows/functional-networking.yaml index 7c2f946359..190b10278e 100644 --- a/.github/workflows/functional-networking.yaml +++ b/.github/workflows/functional-networking.yaml @@ -15,6 +15,10 @@ jobs: ubuntu_version: ["20.04"] devstack_conf_overrides: [""] include: + - name: "yoga" + openstack_version: "stable/yoga" + ubuntu_version: "20.04" + devstack_conf_overrides: "" - name: "xena" openstack_version: "stable/xena" ubuntu_version: "20.04" diff --git a/.github/workflows/functional-objectstorage.yaml b/.github/workflows/functional-objectstorage.yaml index 6e1f365efb..bc577cb59c 100644 --- a/.github/workflows/functional-objectstorage.yaml +++ b/.github/workflows/functional-objectstorage.yaml @@ -14,6 +14,9 @@ jobs: openstack_version: ["master"] ubuntu_version: ["20.04"] include: + - name: "yoga" + openstack_version: "stable/yoga" + ubuntu_version: "20.04" - name: "xena" openstack_version: "stable/xena" ubuntu_version: "20.04" diff --git a/.github/workflows/functional-orchestration.yaml b/.github/workflows/functional-orchestration.yaml index 0ef49976b3..89e2ee4c4b 100644 --- a/.github/workflows/functional-orchestration.yaml +++ b/.github/workflows/functional-orchestration.yaml @@ -14,6 +14,9 @@ jobs: openstack_version: ["master"] ubuntu_version: ["20.04"] include: + - name: "yoga" + openstack_version: "stable/yoga" + ubuntu_version: "20.04" - name: "xena" openstack_version: "stable/xena" ubuntu_version: "20.04" diff --git a/.github/workflows/functional-placement.yaml b/.github/workflows/functional-placement.yaml index 2b46e7ead6..46fa8863cb 100644 --- a/.github/workflows/functional-placement.yaml +++ b/.github/workflows/functional-placement.yaml @@ -14,6 +14,9 @@ jobs: openstack_version: ["master"] ubuntu_version: ["20.04"] include: + - name: "yoga" + openstack_version: "stable/yoga" + ubuntu_version: "20.04" - name: "xena" openstack_version: "stable/xena" ubuntu_version: "20.04" diff --git a/.github/workflows/functional-sharedfilesystems.yaml b/.github/workflows/functional-sharedfilesystems.yaml index 92b1e94383..4c7091d130 100644 --- a/.github/workflows/functional-sharedfilesystems.yaml +++ b/.github/workflows/functional-sharedfilesystems.yaml @@ -14,6 +14,9 @@ jobs: openstack_version: ["master"] ubuntu_version: ["20.04"] include: + - name: "yoga" + openstack_version: "stable/yoga" + ubuntu_version: "20.04" - name: "xena" openstack_version: "stable/xena" ubuntu_version: "20.04" From a2b45326b4b8d2a410522dc6f76a8740d79353f0 Mon Sep 17 00:00:00 2001 From: Dmitry Tantsur <dtantsur@protonmail.com> Date: Wed, 20 Apr 2022 16:42:06 +0200 Subject: [PATCH 079/360] baremetal: add API to set maintenance and maintenance reason For very historical reasons, Ironic requires a special API to set them. While the maintenance itself can be set directly (for even more historical reasons), the reason cannot. --- .../openstack/baremetal/v1/nodes_test.go | 32 ++++++++++++++ openstack/baremetal/v1/nodes/requests.go | 44 +++++++++++++++++++ openstack/baremetal/v1/nodes/results.go | 6 +++ .../baremetal/v1/nodes/testing/fixtures.go | 25 +++++++++++ .../v1/nodes/testing/requests_test.go | 22 ++++++++++ openstack/baremetal/v1/nodes/urls.go | 4 ++ 6 files changed, 133 insertions(+) diff --git a/acceptance/openstack/baremetal/v1/nodes_test.go b/acceptance/openstack/baremetal/v1/nodes_test.go index 4f26e2d0cf..2c0af7c499 100644 --- a/acceptance/openstack/baremetal/v1/nodes_test.go +++ b/acceptance/openstack/baremetal/v1/nodes_test.go @@ -68,6 +68,38 @@ func TestNodesUpdate(t *testing.T) { th.AssertEquals(t, updated.Maintenance, true) } +func TestNodesMaintenance(t *testing.T) { + clients.RequireLong(t) + + client, err := clients.NewBareMetalV1Client() + th.AssertNoErr(t, err) + client.Microversion = "1.38" + + node, err := CreateNode(t, client) + th.AssertNoErr(t, err) + defer DeleteNode(t, client, node) + + err = nodes.SetMaintenance(client, node.UUID, nodes.MaintenanceOpts{ + Reason: "I'm tired", + }).ExtractErr() + th.AssertNoErr(t, err) + + updated, err := nodes.Get(client, node.UUID).Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, updated.Maintenance, true) + th.AssertEquals(t, updated.MaintenanceReason, "I'm tired") + + err = nodes.UnsetMaintenance(client, node.UUID).ExtractErr() + th.AssertNoErr(t, err) + + updated, err = nodes.Get(client, node.UUID).Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, updated.Maintenance, false) + th.AssertEquals(t, updated.MaintenanceReason, "") +} + func TestNodesRAIDConfig(t *testing.T) { clients.SkipReleasesBelow(t, "stable/ussuri") clients.RequireLong(t) diff --git a/openstack/baremetal/v1/nodes/requests.go b/openstack/baremetal/v1/nodes/requests.go index ed3fe3439f..cd41662427 100644 --- a/openstack/baremetal/v1/nodes/requests.go +++ b/openstack/baremetal/v1/nodes/requests.go @@ -835,3 +835,47 @@ func CreateSubscription(client *gophercloud.ServiceClient, id string, method Cal _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return r } + +// MaintenanceOpts for a request to set the node's maintenance mode. +type MaintenanceOpts struct { + Reason string `json:"reason,omitempty"` +} + +// MaintenanceOptsBuilder allows extensions to add additional parameters to the SetMaintenance request. +type MaintenanceOptsBuilder interface { + ToMaintenanceMap() (map[string]interface{}, error) +} + +// ToMaintenanceMap assembles a request body based on the contents of a MaintenanceOpts. +func (opts MaintenanceOpts) ToMaintenanceMap() (map[string]interface{}, error) { + body, err := gophercloud.BuildRequestBody(opts, "") + if err != nil { + return nil, err + } + + return body, nil +} + +// Request to set the Node's maintenance mode. +func SetMaintenance(client *gophercloud.ServiceClient, id string, opts MaintenanceOptsBuilder) (r SetMaintenanceResult) { + reqBody, err := opts.ToMaintenanceMap() + if err != nil { + r.Err = err + return + } + + resp, err := client.Put(maintenanceURL(client, id), reqBody, nil, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// Request to unset the Node's maintenance mode. +func UnsetMaintenance(client *gophercloud.ServiceClient, id string) (r SetMaintenanceResult) { + resp, err := client.Delete(maintenanceURL(client, id), &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} diff --git a/openstack/baremetal/v1/nodes/results.go b/openstack/baremetal/v1/nodes/results.go index dd02b532e2..f07b1be54a 100644 --- a/openstack/baremetal/v1/nodes/results.go +++ b/openstack/baremetal/v1/nodes/results.go @@ -501,3 +501,9 @@ type SubscriptionVendorPassthru struct { EventTypes []string `json:"EventTypes"` Protocol string `json:"Protocol"` } + +// SetMaintenanceResult is the response from a SetMaintenance operation. Call its ExtractErr +// method to determine if the call succeeded or failed. +type SetMaintenanceResult struct { + gophercloud.ErrResult +} diff --git a/openstack/baremetal/v1/nodes/testing/fixtures.go b/openstack/baremetal/v1/nodes/testing/fixtures.go index 409fbe6342..7dc7c991b2 100644 --- a/openstack/baremetal/v1/nodes/testing/fixtures.go +++ b/openstack/baremetal/v1/nodes/testing/fixtures.go @@ -806,6 +806,12 @@ const NodeCreateSubscriptionVendorPassthruRequiredParametersBody = ` } ` +const NodeSetMaintenanceBody = ` +{ + "reason": "I'm tired" +} +` + var ( NodeFoo = nodes.Node{ UUID: "d2630783-6ec8-4836-b556-ab427c4b581e", @@ -1524,3 +1530,22 @@ func HandleDeleteSubscriptionVendorPassthruSuccessfully(t *testing.T) { w.WriteHeader(http.StatusNoContent) }) } + +func HandleSetNodeMaintenanceSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/nodes/1234asdf/maintenance", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PUT") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + th.TestJSONRequest(t, r, NodeSetMaintenanceBody) + + w.WriteHeader(http.StatusAccepted) + }) +} + +func HandleUnsetNodeMaintenanceSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/nodes/1234asdf/maintenance", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "DELETE") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + w.WriteHeader(http.StatusAccepted) + }) +} diff --git a/openstack/baremetal/v1/nodes/testing/requests_test.go b/openstack/baremetal/v1/nodes/testing/requests_test.go index ac6ded0dd7..ba9e8f3ceb 100644 --- a/openstack/baremetal/v1/nodes/testing/requests_test.go +++ b/openstack/baremetal/v1/nodes/testing/requests_test.go @@ -689,3 +689,25 @@ func TestDeleteSubscription(t *testing.T) { err := nodes.DeleteSubscription(c, "1234asdf", method, deleteOpt).ExtractErr() th.AssertNoErr(t, err) } + +func TestSetMaintenance(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleSetNodeMaintenanceSuccessfully(t) + + c := client.ServiceClient() + err := nodes.SetMaintenance(c, "1234asdf", nodes.MaintenanceOpts{ + Reason: "I'm tired", + }).ExtractErr() + th.AssertNoErr(t, err) +} + +func TestUnsetMaintenance(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleUnsetNodeMaintenanceSuccessfully(t) + + c := client.ServiceClient() + err := nodes.UnsetMaintenance(c, "1234asdf").ExtractErr() + th.AssertNoErr(t, err) +} diff --git a/openstack/baremetal/v1/nodes/urls.go b/openstack/baremetal/v1/nodes/urls.go index 9e67714dd1..ab82db932e 100644 --- a/openstack/baremetal/v1/nodes/urls.go +++ b/openstack/baremetal/v1/nodes/urls.go @@ -73,3 +73,7 @@ func vendorPassthruMethodsURL(client *gophercloud.ServiceClient, id string) stri func vendorPassthruCallURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("nodes", id, "vendor_passthru") } + +func maintenanceURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL("nodes", id, "maintenance") +} From 4710e38901b2e3b20702ceb55272ad858adcd2a3 Mon Sep 17 00:00:00 2001 From: naveen <172697+naveensrinivasan@users.noreply.github.com> Date: Tue, 3 May 2022 01:03:41 +0000 Subject: [PATCH 080/360] chore: Set permissions for GitHub actions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Restrict the GitHub token permissions only to the required ones; this way, even if the attackers will succeed in compromising your workflow, they won’t be able to do much. - Included permissions for the action. https://github.com/ossf/scorecard/blob/main/docs/checks.md#token-permissions https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#permissions https://docs.github.com/en/actions/using-jobs/assigning-permissions-to-jobs [Keeping your GitHub Actions and workflows secure Part 1: Preventing pwn requests](https://securitylab.github.com/research/github-actions-preventing-pwn-requests/) Signed-off-by: naveen <172697+naveensrinivasan@users.noreply.github.com> --- .github/workflows/gomod.yml | 3 +++ .github/workflows/reauth-retests.yaml | 3 +++ .github/workflows/unit.yml | 8 ++++++++ 3 files changed, 14 insertions(+) diff --git a/.github/workflows/gomod.yml b/.github/workflows/gomod.yml index 155e8f7ea2..01234900d1 100644 --- a/.github/workflows/gomod.yml +++ b/.github/workflows/gomod.yml @@ -1,5 +1,8 @@ on: [push, pull_request] name: go mod +permissions: + contents: read + jobs: test: runs-on: ubuntu-latest diff --git a/.github/workflows/reauth-retests.yaml b/.github/workflows/reauth-retests.yaml index bc4a928d9a..4bff02fb7f 100644 --- a/.github/workflows/reauth-retests.yaml +++ b/.github/workflows/reauth-retests.yaml @@ -1,5 +1,8 @@ on: [push, pull_request] name: Reauth retests +permissions: + contents: read + jobs: test: runs-on: ubuntu-latest diff --git a/.github/workflows/unit.yml b/.github/workflows/unit.yml index b74a8f8fd3..093c5be206 100644 --- a/.github/workflows/unit.yml +++ b/.github/workflows/unit.yml @@ -1,7 +1,13 @@ on: [push, pull_request] name: Unit Testing +permissions: + contents: read + jobs: test: + permissions: + checks: write # for shogo82148/actions-goveralls to create a new check based on the results + contents: read # for actions/checkout to fetch code runs-on: ubuntu-latest strategy: fail-fast: false @@ -43,6 +49,8 @@ jobs: parallel: true finish: + permissions: + checks: write # for shogo82148/actions-goveralls to create a new check based on the results needs: test runs-on: ubuntu-latest steps: From 5a0ae9410bf0cc43b0e097499b283c4ba3c75882 Mon Sep 17 00:00:00 2001 From: naveensrinivasan <172697+naveensrinivasan@users.noreply.github.com> Date: Tue, 3 May 2022 21:27:35 -0500 Subject: [PATCH 081/360] chore: Included githubactions in the dependabot config This should help with keeping the GitHub actions updated on new releases. This will also help with keeping it secure. Dependabot helps in keeping the supply chain secure https://docs.github.com/en/code-security/dependabot GitHub actions up to date https://docs.github.com/en/code-security/dependabot/working-with-dependabot/keeping-your-actions-up-to-date-with-dependabot https://github.com/ossf/scorecard/blob/main/docs/checks.md#dependency-update-tool Signed-off-by: naveensrinivasan <172697+naveensrinivasan@users.noreply.github.com> --- .github/dependabot.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index d921d0ffdb..2b5c704536 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -5,3 +5,8 @@ updates: schedule: interval: daily open-pull-requests-limit: 10 +- package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: daily + open-pull-requests-limit: 10 From 37599027717bcc45a578ed6677305056d365fef3 Mon Sep 17 00:00:00 2001 From: shhgs <shhgs@yahoo.com> Date: Tue, 3 May 2022 23:14:23 -0400 Subject: [PATCH 082/360] Neutron v2: BGP Peer create / delete --- .github/workflows/functional-networking.yaml | 1 + .../v2/extensions/bgp/peers/bgppeers_test.go | 66 +++++++++++++++++++ .../networking/v2/extensions/bgp/peers/doc.go | 2 + .../networking/v2/extensions/bgp/peers/doc.go | 25 +++++++ .../v2/extensions/bgp/peers/requests.go | 39 +++++++++++ .../v2/extensions/bgp/peers/results.go | 12 ++++ .../extensions/bgp/peers/testing/fixture.go | 26 ++++++++ .../bgp/peers/testing/requests_test.go | 47 +++++++++++++ .../v2/extensions/bgp/peers/urls.go | 10 +++ 9 files changed, 228 insertions(+) create mode 100644 acceptance/openstack/networking/v2/extensions/bgp/peers/bgppeers_test.go create mode 100644 acceptance/openstack/networking/v2/extensions/bgp/peers/doc.go diff --git a/.github/workflows/functional-networking.yaml b/.github/workflows/functional-networking.yaml index 190b10278e..2f713b5f8b 100644 --- a/.github/workflows/functional-networking.yaml +++ b/.github/workflows/functional-networking.yaml @@ -53,6 +53,7 @@ jobs: conf_overrides: | Q_ML2_PLUGIN_EXT_DRIVERS=qos,port_security,dns_domain_keywords enable_plugin neutron-vpnaas https://opendev.org/openstack/neutron-vpnaas ${{ matrix.openstack_version }} + enable_plugin neutron-dynamic-routing https://opendev.org/openstack/neutron-dynamic-routing ${{ matrix.openstack_version }} ${{ matrix.devstack_conf_overrides }} enabled_services: 'neutron-dns,neutron-qos,neutron-segments,neutron-trunk,neutron-uplink-status-propagation,neutron-network-segment-range,neutron-port-forwarding' - name: Checkout go diff --git a/acceptance/openstack/networking/v2/extensions/bgp/peers/bgppeers_test.go b/acceptance/openstack/networking/v2/extensions/bgp/peers/bgppeers_test.go new file mode 100644 index 0000000000..07f0bc3801 --- /dev/null +++ b/acceptance/openstack/networking/v2/extensions/bgp/peers/bgppeers_test.go @@ -0,0 +1,66 @@ +package peers + +import ( + "testing" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/bgp/peers" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func CreateBGPPeer(t *testing.T, client *gophercloud.ServiceClient) (*peers.BGPPeer, error) { + var opts peers.CreateOpts + opts.AuthType = "md5" + opts.Password = tools.MakeNewPassword("") + opts.RemoteAS = tools.RandomInt(1000, 2000) + opts.Name = tools.RandomString("TESTACC-BGPPEER-", 8) + opts.PeerIP = "192.168.0.1" + + t.Logf("Attempting to create BGP Peer: %s", opts.Name) + bgpPeer, err := peers.Create(client, opts).Extract() + if err != nil { + return bgpPeer, err + } + + t.Logf("Successfully created BGP Peer") + th.AssertEquals(t, bgpPeer.Name, opts.Name) + th.AssertEquals(t, bgpPeer.RemoteAS, opts.RemoteAS) + th.AssertEquals(t, bgpPeer.PeerIP, opts.PeerIP) + return bgpPeer, err +} + +func TestBGPPeerCRD(t *testing.T) { + clients.RequireAdmin(t) + + client, err := clients.NewNetworkV2Client() + th.AssertNoErr(t, err) + + allPages, err := peers.List(client).AllPages() + th.AssertNoErr(t, err) + + allPeers, err := peers.ExtractBGPPeers(allPages) + th.AssertNoErr(t, err) + + t.Logf("Retrieved BGP Peers") + tools.PrintResource(t, allPeers) + + bgpPeerCreated, err := CreateBGPPeer(t, client) + th.AssertNoErr(t, err) + + tools.PrintResource(t, bgpPeerCreated) + + bgpPeerGot, err := peers.Get(client, bgpPeerCreated.ID).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, bgpPeerCreated.ID, bgpPeerGot.ID) + th.AssertEquals(t, bgpPeerCreated.Name, bgpPeerGot.Name) + + t.Logf("Attempting to delete BGP Peer: %s", bgpPeerGot.Name) + err = peers.Delete(client, bgpPeerGot.ID).ExtractErr() + th.AssertNoErr(t, err) + + bgpPeerGot, err = peers.Get(client, bgpPeerGot.ID).Extract() + th.AssertErr(t, err) + t.Logf("BGP Peer %s deleted", bgpPeerCreated.Name) +} diff --git a/acceptance/openstack/networking/v2/extensions/bgp/peers/doc.go b/acceptance/openstack/networking/v2/extensions/bgp/peers/doc.go new file mode 100644 index 0000000000..7830a4d1e8 --- /dev/null +++ b/acceptance/openstack/networking/v2/extensions/bgp/peers/doc.go @@ -0,0 +1,2 @@ +// BGP Peer acceptance tests +package peers diff --git a/openstack/networking/v2/extensions/bgp/peers/doc.go b/openstack/networking/v2/extensions/bgp/peers/doc.go index 9a6af137aa..f55764aa5b 100644 --- a/openstack/networking/v2/extensions/bgp/peers/doc.go +++ b/openstack/networking/v2/extensions/bgp/peers/doc.go @@ -29,4 +29,29 @@ Example: log.Panic(err) } log.Printf("%+v", *p) + +3. Create BGP Peer, a.k.a. POST /bgp-peers + +Example: + var opts peers.CreateOpts + opts.AuthType = "md5" + opts.Password = "notSoStrong" + opts.RemoteAS = 20000 + opts.Name = "gophercloud-testing-bgp-peer" + opts.PeerIP = "192.168.0.1" + r, err := peers.Create(c, opts).Extract() + if err != nil { + log.Panic(err) + } + log.Printf("%+v", *r) + +4. Delete BGP Peer, a.k.a. DELETE /bgp-peers/{id} + +Example: + + err := peers.Delete(c, bgpPeerID).ExtractErr() + if err != nil { + log.Panic(err) + } + log.Printf("BGP Peer deleted") */ diff --git a/openstack/networking/v2/extensions/bgp/peers/requests.go b/openstack/networking/v2/extensions/bgp/peers/requests.go index e9a1c8c41c..480c4c584c 100644 --- a/openstack/networking/v2/extensions/bgp/peers/requests.go +++ b/openstack/networking/v2/extensions/bgp/peers/requests.go @@ -19,3 +19,42 @@ func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } + +// CreateOptsBuilder allows extensions to add additional parameters to the +// Create request. +type CreateOptsBuilder interface { + ToPeerCreateMap() (map[string]interface{}, error) +} + +// CreateOpts represents options used to create a network. +type CreateOpts struct { + AuthType string `json:"auth_type"` + RemoteAS int `json:"remote_as"` + Name string `json:"name"` + Password string `json:"password,omitempty"` + PeerIP string `json:"peer_ip"` +} + +// ToPeerCreateMap builds a request body from CreateOpts. +func (opts CreateOpts) ToPeerCreateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, jroot) +} + +// Create a BGP Peer +func Create(c *gophercloud.ServiceClient, opts CreateOpts) (r CreateResult) { + b, err := opts.ToPeerCreateMap() + if err != nil { + r.Err = err + return + } + resp, err := c.Post(createURL(c), b, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// Delete accepts a unique ID and deletes the bgp Peer associated with it. +func Delete(c *gophercloud.ServiceClient, bgpPeerID string) (r DeleteResult) { + resp, err := c.Delete(deleteURL(c, bgpPeerID), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} diff --git a/openstack/networking/v2/extensions/bgp/peers/results.go b/openstack/networking/v2/extensions/bgp/peers/results.go index 5574c5e410..8fc9f04392 100644 --- a/openstack/networking/v2/extensions/bgp/peers/results.go +++ b/openstack/networking/v2/extensions/bgp/peers/results.go @@ -76,3 +76,15 @@ func ExtractBGPPeersInto(r pagination.Page, v interface{}) error { type GetResult struct { commonResult } + +// CreateResult represents the result of a create operation. Call its Extract +// method to intepret it as a BGPPeer. +type CreateResult struct { + commonResult +} + +// DeleteResult represents the result of a delete operation. Call its +// ExtractErr method to determine if the request succeeded or failed. +type DeleteResult struct { + gophercloud.ErrResult +} diff --git a/openstack/networking/v2/extensions/bgp/peers/testing/fixture.go b/openstack/networking/v2/extensions/bgp/peers/testing/fixture.go index 8396e6ec55..86d2e0d3bd 100644 --- a/openstack/networking/v2/extensions/bgp/peers/testing/fixture.go +++ b/openstack/networking/v2/extensions/bgp/peers/testing/fixture.go @@ -60,3 +60,29 @@ const GetBGPPeerResult = ` } } ` + +const CreateRequest = ` +{ + "bgp_peer": { + "auth_type": "md5", + "name": "gophercloud-testing-bgp-peer", + "password": "notSoStrong", + "peer_ip": "192.168.0.1", + "remote_as": 20000 + } +} +` + +const CreateResponse = ` +{ + "bgp_peer": { + "auth_type": "md5", + "project_id": "52a9d4ff-81b6-4b16-a7fa-5325d3bc1c5d", + "remote_as": 20000, + "name": "gophercloud-testing-bgp-peer", + "tenant_id": "52a9d4ff-81b6-4b16-a7fa-5325d3bc1c5d", + "peer_ip": "192.168.0.1", + "id": "b7ad63ea-b803-496a-ad59-f9ef513a5cb9" + } +} +` diff --git a/openstack/networking/v2/extensions/bgp/peers/testing/requests_test.go b/openstack/networking/v2/extensions/bgp/peers/testing/requests_test.go index 980ecaaedb..46ffc94294 100644 --- a/openstack/networking/v2/extensions/bgp/peers/testing/requests_test.go +++ b/openstack/networking/v2/extensions/bgp/peers/testing/requests_test.go @@ -58,3 +58,50 @@ func TestGet(t *testing.T) { th.AssertNoErr(t, err) th.CheckDeepEquals(t, *s, BGPPeer1) } + +func TestCreate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/bgp-peers", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestHeader(t, r, "Content-Type", "application/json") + th.TestHeader(t, r, "Accept", "application/json") + th.TestJSONRequest(t, r, CreateRequest) + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusCreated) + fmt.Fprintf(w, CreateResponse) + }) + + var opts peers.CreateOpts + opts.AuthType = "md5" + opts.Password = "notSoStrong" + opts.RemoteAS = 20000 + opts.Name = "gophercloud-testing-bgp-peer" + opts.PeerIP = "192.168.0.1" + + r, err := peers.Create(fake.ServiceClient(), opts).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, r.AuthType, opts.AuthType) + th.AssertEquals(t, r.RemoteAS, opts.RemoteAS) + th.AssertEquals(t, r.PeerIP, opts.PeerIP) +} + +func TestDelete(t *testing.T) { + bgpPeerID := "afacc0e8-6b66-44e4-be53-a1ef16033ceb" + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/bgp-peers/"+bgpPeerID, func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "DELETE") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestHeader(t, r, "Accept", "application/json") + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusNoContent) + }) + + err := peers.Delete(fake.ServiceClient(), bgpPeerID).ExtractErr() + th.AssertNoErr(t, err) +} diff --git a/openstack/networking/v2/extensions/bgp/peers/urls.go b/openstack/networking/v2/extensions/bgp/peers/urls.go index 43a288311e..040617b0fb 100644 --- a/openstack/networking/v2/extensions/bgp/peers/urls.go +++ b/openstack/networking/v2/extensions/bgp/peers/urls.go @@ -23,3 +23,13 @@ func getURL(c *gophercloud.ServiceClient, id string) string { func listURL(c *gophercloud.ServiceClient) string { return rootURL(c) } + +// return /v2.0/bgp-peers +func createURL(c *gophercloud.ServiceClient) string { + return rootURL(c) +} + +// return /v2.0/bgp-peers/{bgp-peer-id} +func deleteURL(c *gophercloud.ServiceClient, id string) string { + return resourceURL(c, id) +} From faf09b7e5ef73a4234fd9d131c88870bb96aa4ee Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 4 May 2022 07:44:27 +0000 Subject: [PATCH 083/360] Bump actions/checkout from 2 to 3 Bumps [actions/checkout](https://github.com/actions/checkout) from 2 to 3. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v2...v3) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> --- .github/workflows/codeql-analysis.yml | 2 +- .github/workflows/functional-baremetal.yaml | 2 +- .github/workflows/functional-basic.yaml | 2 +- .github/workflows/functional-blockstorage.yaml | 2 +- .github/workflows/functional-clustering.yaml | 2 +- .github/workflows/functional-compute.yaml | 2 +- .github/workflows/functional-dns.yaml | 2 +- .github/workflows/functional-identity.yaml | 2 +- .github/workflows/functional-imageservice.yaml | 2 +- .github/workflows/functional-keymanager.yaml | 2 +- .github/workflows/functional-loadbalancer.yaml | 2 +- .github/workflows/functional-messaging.yaml | 2 +- .github/workflows/functional-networking.yaml | 2 +- .github/workflows/functional-objectstorage.yaml | 2 +- .github/workflows/functional-orchestration.yaml | 2 +- .github/workflows/functional-placement.yaml | 2 +- .github/workflows/functional-sharedfilesystems.yaml | 2 +- .github/workflows/gomod.yml | 2 +- .github/workflows/reauth-retests.yaml | 2 +- .github/workflows/unit.yml | 2 +- 20 files changed, 20 insertions(+), 20 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 5ab08fab9e..1ee1ede517 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -25,7 +25,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Initialize CodeQL uses: github/codeql-action/init@v1 diff --git a/.github/workflows/functional-baremetal.yaml b/.github/workflows/functional-baremetal.yaml index 02b0c76c45..9c8dc65f39 100644 --- a/.github/workflows/functional-baremetal.yaml +++ b/.github/workflows/functional-baremetal.yaml @@ -36,7 +36,7 @@ jobs: name: Deploy OpenStack ${{ matrix.name }} with Ironic and run baremetal acceptance tests steps: - name: Checkout Gophercloud - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Deploy devstack uses: EmilienM/devstack-action@v0.6 with: diff --git a/.github/workflows/functional-basic.yaml b/.github/workflows/functional-basic.yaml index 574e15f23a..d26ae482bf 100644 --- a/.github/workflows/functional-basic.yaml +++ b/.github/workflows/functional-basic.yaml @@ -39,7 +39,7 @@ jobs: name: Deploy OpenStack ${{ matrix.name }} with defaults and run basic acceptance tests steps: - name: Checkout Gophercloud - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Deploy devstack uses: EmilienM/devstack-action@v0.6 with: diff --git a/.github/workflows/functional-blockstorage.yaml b/.github/workflows/functional-blockstorage.yaml index 933dd433dc..642dd1d48d 100644 --- a/.github/workflows/functional-blockstorage.yaml +++ b/.github/workflows/functional-blockstorage.yaml @@ -36,7 +36,7 @@ jobs: name: Deploy OpenStack ${{ matrix.name }} with Cinder and run blockstorage acceptance tests steps: - name: Checkout Gophercloud - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Deploy devstack uses: EmilienM/devstack-action@v0.6 with: diff --git a/.github/workflows/functional-clustering.yaml b/.github/workflows/functional-clustering.yaml index 766ca770b7..cd6342c8fa 100644 --- a/.github/workflows/functional-clustering.yaml +++ b/.github/workflows/functional-clustering.yaml @@ -36,7 +36,7 @@ jobs: name: Deploy OpenStack ${{ matrix.name }} with Senlin and run clustering acceptance tests steps: - name: Checkout Gophercloud - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Deploy devstack uses: EmilienM/devstack-action@v0.6 with: diff --git a/.github/workflows/functional-compute.yaml b/.github/workflows/functional-compute.yaml index a5bc4d317d..4d905473ac 100644 --- a/.github/workflows/functional-compute.yaml +++ b/.github/workflows/functional-compute.yaml @@ -36,7 +36,7 @@ jobs: name: Deploy OpenStack ${{ matrix.name }} with Nova and run compute acceptance tests steps: - name: Checkout Gophercloud - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Deploy devstack uses: EmilienM/devstack-action@v0.6 with: diff --git a/.github/workflows/functional-dns.yaml b/.github/workflows/functional-dns.yaml index 13bc091978..ec70b47d8c 100644 --- a/.github/workflows/functional-dns.yaml +++ b/.github/workflows/functional-dns.yaml @@ -37,7 +37,7 @@ jobs: name: Deploy OpenStack ${{ matrix.name }} with Designate and run dns acceptance tests steps: - name: Checkout Gophercloud - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Deploy devstack uses: EmilienM/devstack-action@v0.6 with: diff --git a/.github/workflows/functional-identity.yaml b/.github/workflows/functional-identity.yaml index 6a73c87b71..296e2c14bd 100644 --- a/.github/workflows/functional-identity.yaml +++ b/.github/workflows/functional-identity.yaml @@ -36,7 +36,7 @@ jobs: name: Deploy OpenStack ${{ matrix.name }} with Keystone and run identity acceptance tests steps: - name: Checkout Gophercloud - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Deploy devstack uses: EmilienM/devstack-action@v0.6 with: diff --git a/.github/workflows/functional-imageservice.yaml b/.github/workflows/functional-imageservice.yaml index 2bacedcdf6..1aa1b93d69 100644 --- a/.github/workflows/functional-imageservice.yaml +++ b/.github/workflows/functional-imageservice.yaml @@ -36,7 +36,7 @@ jobs: name: Deploy OpenStack ${{ matrix.name }} with Glance and run imageservice acceptance tests steps: - name: Checkout Gophercloud - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Deploy devstack uses: EmilienM/devstack-action@v0.6 with: diff --git a/.github/workflows/functional-keymanager.yaml b/.github/workflows/functional-keymanager.yaml index 20c4b79d26..3c2678f12b 100644 --- a/.github/workflows/functional-keymanager.yaml +++ b/.github/workflows/functional-keymanager.yaml @@ -36,7 +36,7 @@ jobs: name: Deploy OpenStack ${{ matrix.name }} with Barbican and run keymanager acceptance tests steps: - name: Checkout Gophercloud - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Deploy devstack uses: EmilienM/devstack-action@v0.6 with: diff --git a/.github/workflows/functional-loadbalancer.yaml b/.github/workflows/functional-loadbalancer.yaml index 72479bd4fe..c1f48f556a 100644 --- a/.github/workflows/functional-loadbalancer.yaml +++ b/.github/workflows/functional-loadbalancer.yaml @@ -36,7 +36,7 @@ jobs: name: Deploy OpenStack ${{ matrix.name }} with Octavia and run loadbalancer acceptance tests steps: - name: Checkout Gophercloud - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Deploy devstack uses: EmilienM/devstack-action@v0.6 with: diff --git a/.github/workflows/functional-messaging.yaml b/.github/workflows/functional-messaging.yaml index 89c728eedc..4588b37b54 100644 --- a/.github/workflows/functional-messaging.yaml +++ b/.github/workflows/functional-messaging.yaml @@ -36,7 +36,7 @@ jobs: name: Deploy OpenStack ${{ matrix.name }} with Zaqar and run messaging acceptance tests steps: - name: Checkout Gophercloud - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Deploy devstack uses: EmilienM/devstack-action@v0.6 with: diff --git a/.github/workflows/functional-networking.yaml b/.github/workflows/functional-networking.yaml index 190b10278e..887748778a 100644 --- a/.github/workflows/functional-networking.yaml +++ b/.github/workflows/functional-networking.yaml @@ -45,7 +45,7 @@ jobs: name: Deploy OpenStack ${{ matrix.name }} with Neutron and run networking acceptance tests steps: - name: Checkout Gophercloud - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Deploy devstack uses: EmilienM/devstack-action@v0.6 with: diff --git a/.github/workflows/functional-objectstorage.yaml b/.github/workflows/functional-objectstorage.yaml index bc577cb59c..a4e6f5f2e9 100644 --- a/.github/workflows/functional-objectstorage.yaml +++ b/.github/workflows/functional-objectstorage.yaml @@ -36,7 +36,7 @@ jobs: name: Deploy OpenStack ${{ matrix.name }} with Swift and run objectstorage acceptance tests steps: - name: Checkout Gophercloud - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Deploy devstack uses: EmilienM/devstack-action@v0.6 with: diff --git a/.github/workflows/functional-orchestration.yaml b/.github/workflows/functional-orchestration.yaml index 89e2ee4c4b..27f371603b 100644 --- a/.github/workflows/functional-orchestration.yaml +++ b/.github/workflows/functional-orchestration.yaml @@ -36,7 +36,7 @@ jobs: name: Deploy OpenStack ${{ matrix.name }} with Heat and run orchestration acceptance tests steps: - name: Checkout Gophercloud - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Deploy devstack uses: EmilienM/devstack-action@v0.6 with: diff --git a/.github/workflows/functional-placement.yaml b/.github/workflows/functional-placement.yaml index 46fa8863cb..98f8230d5c 100644 --- a/.github/workflows/functional-placement.yaml +++ b/.github/workflows/functional-placement.yaml @@ -36,7 +36,7 @@ jobs: name: Deploy OpenStack ${{ matrix.name }} with Placement and run placement acceptance tests steps: - name: Checkout Gophercloud - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Deploy devstack uses: EmilienM/devstack-action@v0.6 with: diff --git a/.github/workflows/functional-sharedfilesystems.yaml b/.github/workflows/functional-sharedfilesystems.yaml index 4c7091d130..ef348cbd3c 100644 --- a/.github/workflows/functional-sharedfilesystems.yaml +++ b/.github/workflows/functional-sharedfilesystems.yaml @@ -36,7 +36,7 @@ jobs: name: Deploy OpenStack ${{ matrix.name }} with Manila and run sharedfilesystems acceptance tests steps: - name: Checkout Gophercloud - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Deploy devstack uses: EmilienM/devstack-action@v0.6 with: diff --git a/.github/workflows/gomod.yml b/.github/workflows/gomod.yml index 01234900d1..b96cd876c1 100644 --- a/.github/workflows/gomod.yml +++ b/.github/workflows/gomod.yml @@ -7,7 +7,7 @@ jobs: test: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: actions/setup-go@v2 with: go-version: '1' diff --git a/.github/workflows/reauth-retests.yaml b/.github/workflows/reauth-retests.yaml index 4bff02fb7f..39130cc6ef 100644 --- a/.github/workflows/reauth-retests.yaml +++ b/.github/workflows/reauth-retests.yaml @@ -18,7 +18,7 @@ jobs: with: go-version: ${{ matrix.go-version }} - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Run reauth retests run: | diff --git a/.github/workflows/unit.yml b/.github/workflows/unit.yml index 093c5be206..3c5deace5d 100644 --- a/.github/workflows/unit.yml +++ b/.github/workflows/unit.yml @@ -25,7 +25,7 @@ jobs: with: go-version: ${{ matrix.go-version }} - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Setup environment run: | From 8c4b423b4d5028d7bb9d75d6a60b9580aea331a5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 4 May 2022 07:44:30 +0000 Subject: [PATCH 084/360] Bump actions/upload-artifact from 2 to 3 Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 2 to 3. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/v2...v3) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> --- .github/workflows/functional-baremetal.yaml | 2 +- .github/workflows/functional-basic.yaml | 2 +- .github/workflows/functional-blockstorage.yaml | 2 +- .github/workflows/functional-clustering.yaml | 2 +- .github/workflows/functional-compute.yaml | 2 +- .github/workflows/functional-dns.yaml | 2 +- .github/workflows/functional-identity.yaml | 2 +- .github/workflows/functional-imageservice.yaml | 2 +- .github/workflows/functional-keymanager.yaml | 2 +- .github/workflows/functional-loadbalancer.yaml | 2 +- .github/workflows/functional-messaging.yaml | 2 +- .github/workflows/functional-networking.yaml | 2 +- .github/workflows/functional-objectstorage.yaml | 2 +- .github/workflows/functional-orchestration.yaml | 2 +- .github/workflows/functional-placement.yaml | 2 +- .github/workflows/functional-sharedfilesystems.yaml | 2 +- 16 files changed, 16 insertions(+), 16 deletions(-) diff --git a/.github/workflows/functional-baremetal.yaml b/.github/workflows/functional-baremetal.yaml index 02b0c76c45..bba588404e 100644 --- a/.github/workflows/functional-baremetal.yaml +++ b/.github/workflows/functional-baremetal.yaml @@ -89,7 +89,7 @@ jobs: if: failure() - name: Upload logs artifacts on failure if: failure() - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: functional-baremetal-${{ matrix.name }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-basic.yaml b/.github/workflows/functional-basic.yaml index 574e15f23a..be9784dbc7 100644 --- a/.github/workflows/functional-basic.yaml +++ b/.github/workflows/functional-basic.yaml @@ -59,7 +59,7 @@ jobs: if: failure() - name: Upload logs artifacts on failure if: failure() - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: functional-basic-${{ matrix.name }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-blockstorage.yaml b/.github/workflows/functional-blockstorage.yaml index 933dd433dc..4b3526092e 100644 --- a/.github/workflows/functional-blockstorage.yaml +++ b/.github/workflows/functional-blockstorage.yaml @@ -59,7 +59,7 @@ jobs: if: failure() - name: Upload logs artifacts on failure if: failure() - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: functional-blockstorage-${{ matrix.name }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-clustering.yaml b/.github/workflows/functional-clustering.yaml index 766ca770b7..a310b46c81 100644 --- a/.github/workflows/functional-clustering.yaml +++ b/.github/workflows/functional-clustering.yaml @@ -58,7 +58,7 @@ jobs: if: failure() - name: Upload logs artifacts on failure if: failure() - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: functional-clustering-${{ matrix.name }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-compute.yaml b/.github/workflows/functional-compute.yaml index a5bc4d317d..9c841afa39 100644 --- a/.github/workflows/functional-compute.yaml +++ b/.github/workflows/functional-compute.yaml @@ -57,7 +57,7 @@ jobs: if: failure() - name: Upload logs artifacts on failure if: failure() - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: functional-compute-${{ matrix.name }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-dns.yaml b/.github/workflows/functional-dns.yaml index 13bc091978..09b60c69fb 100644 --- a/.github/workflows/functional-dns.yaml +++ b/.github/workflows/functional-dns.yaml @@ -59,7 +59,7 @@ jobs: if: failure() - name: Upload logs artifacts on failure if: failure() - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: functional-dns-${{ matrix.name }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-identity.yaml b/.github/workflows/functional-identity.yaml index 6a73c87b71..47860b8d79 100644 --- a/.github/workflows/functional-identity.yaml +++ b/.github/workflows/functional-identity.yaml @@ -55,7 +55,7 @@ jobs: if: failure() - name: Upload logs artifacts on failure if: failure() - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: functional-identity-${{ matrix.name }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-imageservice.yaml b/.github/workflows/functional-imageservice.yaml index 2bacedcdf6..f95843b4af 100644 --- a/.github/workflows/functional-imageservice.yaml +++ b/.github/workflows/functional-imageservice.yaml @@ -55,7 +55,7 @@ jobs: if: failure() - name: Upload logs artifacts on failure if: failure() - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: functional-imageservice-${{ matrix.name }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-keymanager.yaml b/.github/workflows/functional-keymanager.yaml index 20c4b79d26..1dd61cc731 100644 --- a/.github/workflows/functional-keymanager.yaml +++ b/.github/workflows/functional-keymanager.yaml @@ -58,7 +58,7 @@ jobs: if: failure() - name: Upload logs artifacts on failure if: failure() - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: functional-keymanager-${{ matrix.name }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-loadbalancer.yaml b/.github/workflows/functional-loadbalancer.yaml index 72479bd4fe..53f93928a0 100644 --- a/.github/workflows/functional-loadbalancer.yaml +++ b/.github/workflows/functional-loadbalancer.yaml @@ -60,7 +60,7 @@ jobs: if: failure() - name: Upload logs artifacts on failure if: failure() - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: functional-loadbalancer-${{ matrix.name }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-messaging.yaml b/.github/workflows/functional-messaging.yaml index 89c728eedc..735e4d6d2b 100644 --- a/.github/workflows/functional-messaging.yaml +++ b/.github/workflows/functional-messaging.yaml @@ -57,7 +57,7 @@ jobs: if: failure() - name: Upload logs artifacts on failure if: failure() - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: functional-messaging-${{ matrix.name }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-networking.yaml b/.github/workflows/functional-networking.yaml index 190b10278e..6cb26c5798 100644 --- a/.github/workflows/functional-networking.yaml +++ b/.github/workflows/functional-networking.yaml @@ -70,7 +70,7 @@ jobs: if: failure() - name: Upload logs artifacts on failure if: failure() - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: functional-networking-${{ matrix.name }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-objectstorage.yaml b/.github/workflows/functional-objectstorage.yaml index bc577cb59c..91cd5fb630 100644 --- a/.github/workflows/functional-objectstorage.yaml +++ b/.github/workflows/functional-objectstorage.yaml @@ -59,7 +59,7 @@ jobs: if: failure() - name: Upload logs artifacts on failure if: failure() - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: functional-objectstorage-${{ matrix.name }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-orchestration.yaml b/.github/workflows/functional-orchestration.yaml index 89e2ee4c4b..1d6ed9f331 100644 --- a/.github/workflows/functional-orchestration.yaml +++ b/.github/workflows/functional-orchestration.yaml @@ -58,7 +58,7 @@ jobs: if: failure() - name: Upload logs artifacts on failure if: failure() - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: functional-orchestration-${{ matrix.name }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-placement.yaml b/.github/workflows/functional-placement.yaml index 46fa8863cb..e1ca7ce47c 100644 --- a/.github/workflows/functional-placement.yaml +++ b/.github/workflows/functional-placement.yaml @@ -55,7 +55,7 @@ jobs: if: failure() - name: Upload logs artifacts on failure if: failure() - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: functional-placement-${{ matrix.name }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-sharedfilesystems.yaml b/.github/workflows/functional-sharedfilesystems.yaml index 4c7091d130..92258c0a84 100644 --- a/.github/workflows/functional-sharedfilesystems.yaml +++ b/.github/workflows/functional-sharedfilesystems.yaml @@ -70,7 +70,7 @@ jobs: if: failure() - name: Upload logs artifacts on failure if: failure() - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: functional-sharedfilesystems-${{ matrix.name }} path: /tmp/devstack-logs/* From 8241230f0340d63630f7cb9d0a92457bc2fa0f59 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 4 May 2022 14:35:11 +0000 Subject: [PATCH 085/360] Bump github/codeql-action from 1 to 2 Bumps [github/codeql-action](https://github.com/github/codeql-action) from 1 to 2. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/v1...v2) --- updated-dependencies: - dependency-name: github/codeql-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> --- .github/workflows/codeql-analysis.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 1ee1ede517..db762de4d7 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -28,12 +28,12 @@ jobs: uses: actions/checkout@v3 - name: Initialize CodeQL - uses: github/codeql-action/init@v1 + uses: github/codeql-action/init@v2 with: languages: ${{ matrix.language }} - name: Autobuild - uses: github/codeql-action/autobuild@v1 + uses: github/codeql-action/autobuild@v2 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v1 + uses: github/codeql-action/analyze@v2 From a17d0394184eaba1d6f555b3902b2f46d814a5c4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 4 May 2022 14:35:11 +0000 Subject: [PATCH 086/360] Bump actions/setup-go from 2 to 3 Bumps [actions/setup-go](https://github.com/actions/setup-go) from 2 to 3. - [Release notes](https://github.com/actions/setup-go/releases) - [Commits](https://github.com/actions/setup-go/compare/v2...v3) --- updated-dependencies: - dependency-name: actions/setup-go dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> --- .github/workflows/functional-baremetal.yaml | 2 +- .github/workflows/functional-basic.yaml | 2 +- .github/workflows/functional-blockstorage.yaml | 2 +- .github/workflows/functional-clustering.yaml | 2 +- .github/workflows/functional-compute.yaml | 2 +- .github/workflows/functional-dns.yaml | 2 +- .github/workflows/functional-identity.yaml | 2 +- .github/workflows/functional-imageservice.yaml | 2 +- .github/workflows/functional-keymanager.yaml | 2 +- .github/workflows/functional-loadbalancer.yaml | 2 +- .github/workflows/functional-messaging.yaml | 2 +- .github/workflows/functional-networking.yaml | 2 +- .github/workflows/functional-objectstorage.yaml | 2 +- .github/workflows/functional-orchestration.yaml | 2 +- .github/workflows/functional-placement.yaml | 2 +- .github/workflows/functional-sharedfilesystems.yaml | 2 +- .github/workflows/gomod.yml | 2 +- .github/workflows/reauth-retests.yaml | 2 +- .github/workflows/unit.yml | 2 +- 19 files changed, 19 insertions(+), 19 deletions(-) diff --git a/.github/workflows/functional-baremetal.yaml b/.github/workflows/functional-baremetal.yaml index 9c8dc65f39..b2b8f46f7b 100644 --- a/.github/workflows/functional-baremetal.yaml +++ b/.github/workflows/functional-baremetal.yaml @@ -75,7 +75,7 @@ jobs: SWIFT_TEMPURL_KEY=secretkey enabled_services: 'ir-api,ir-cond,s-account,s-container,s-object,s-proxy,q-svc,q-agt,q-dhcp,q-l3,q-meta,-cinder,-c-sch,-c-api,-c-vol,-c-bak,-ovn,-ovn-controller,-ovn-northd,-q-ovn-metadata-agent' - name: Checkout go - uses: actions/setup-go@v2 + uses: actions/setup-go@v3 with: go-version: '^1.15' - name: Run Gophercloud acceptance tests diff --git a/.github/workflows/functional-basic.yaml b/.github/workflows/functional-basic.yaml index d26ae482bf..33293e725b 100644 --- a/.github/workflows/functional-basic.yaml +++ b/.github/workflows/functional-basic.yaml @@ -46,7 +46,7 @@ jobs: branch: ${{ matrix.openstack_version }} enabled_services: 's-account,s-container,s-object,s-proxy' - name: Checkout go - uses: actions/setup-go@v2 + uses: actions/setup-go@v3 with: go-version: '^1.15' - name: Run Gophercloud acceptance tests diff --git a/.github/workflows/functional-blockstorage.yaml b/.github/workflows/functional-blockstorage.yaml index 642dd1d48d..0c76b5b67e 100644 --- a/.github/workflows/functional-blockstorage.yaml +++ b/.github/workflows/functional-blockstorage.yaml @@ -45,7 +45,7 @@ jobs: CINDER_ISCSI_HELPER=tgtadm enabled_services: 's-account,s-container,s-object,s-proxy,c-bak' - name: Checkout go - uses: actions/setup-go@v2 + uses: actions/setup-go@v3 with: go-version: '^1.15' - name: Run Gophercloud acceptance tests diff --git a/.github/workflows/functional-clustering.yaml b/.github/workflows/functional-clustering.yaml index cd6342c8fa..c4a3d995aa 100644 --- a/.github/workflows/functional-clustering.yaml +++ b/.github/workflows/functional-clustering.yaml @@ -45,7 +45,7 @@ jobs: enable_plugin senlin https://opendev.org/openstack/senlin ${{ matrix.openstack_version }} enable_plugin zaqar https://opendev.org/openstack/zaqar ${{ matrix.openstack_version }} - name: Checkout go - uses: actions/setup-go@v2 + uses: actions/setup-go@v3 with: go-version: '^1.15' - name: Run Gophercloud acceptance tests diff --git a/.github/workflows/functional-compute.yaml b/.github/workflows/functional-compute.yaml index 4d905473ac..0bd9316f12 100644 --- a/.github/workflows/functional-compute.yaml +++ b/.github/workflows/functional-compute.yaml @@ -44,7 +44,7 @@ jobs: conf_overrides: | CINDER_ISCSI_HELPER=tgtadm - name: Checkout go - uses: actions/setup-go@v2 + uses: actions/setup-go@v3 with: go-version: '^1.15' - name: Run Gophercloud acceptance tests diff --git a/.github/workflows/functional-dns.yaml b/.github/workflows/functional-dns.yaml index ec70b47d8c..e094d33798 100644 --- a/.github/workflows/functional-dns.yaml +++ b/.github/workflows/functional-dns.yaml @@ -46,7 +46,7 @@ jobs: enable_plugin designate https://opendev.org/openstack/designate ${{ matrix.openstack_version }} enabled_services: 'designate,designate-central,designate-api,designate-worker,designate-producer,designate-mdns' - name: Checkout go - uses: actions/setup-go@v2 + uses: actions/setup-go@v3 with: go-version: '^1.15' - name: Run Gophercloud acceptance tests diff --git a/.github/workflows/functional-identity.yaml b/.github/workflows/functional-identity.yaml index 296e2c14bd..05fd4ddc59 100644 --- a/.github/workflows/functional-identity.yaml +++ b/.github/workflows/functional-identity.yaml @@ -42,7 +42,7 @@ jobs: with: branch: ${{ matrix.openstack_version }} - name: Checkout go - uses: actions/setup-go@v2 + uses: actions/setup-go@v3 with: go-version: '^1.15' - name: Run Gophercloud acceptance tests diff --git a/.github/workflows/functional-imageservice.yaml b/.github/workflows/functional-imageservice.yaml index 1aa1b93d69..8dfdca688b 100644 --- a/.github/workflows/functional-imageservice.yaml +++ b/.github/workflows/functional-imageservice.yaml @@ -42,7 +42,7 @@ jobs: with: branch: ${{ matrix.openstack_version }} - name: Checkout go - uses: actions/setup-go@v2 + uses: actions/setup-go@v3 with: go-version: '^1.15' - name: Run Gophercloud acceptance tests diff --git a/.github/workflows/functional-keymanager.yaml b/.github/workflows/functional-keymanager.yaml index 3c2678f12b..c6dbb114a5 100644 --- a/.github/workflows/functional-keymanager.yaml +++ b/.github/workflows/functional-keymanager.yaml @@ -45,7 +45,7 @@ jobs: enable_plugin barbican https://opendev.org/openstack/barbican ${{ matrix.openstack_version }} enabled_services: 'barbican-svc,barbican-retry,barbican-keystone-listener' - name: Checkout go - uses: actions/setup-go@v2 + uses: actions/setup-go@v3 with: go-version: '^1.15' - name: Run Gophercloud acceptance tests diff --git a/.github/workflows/functional-loadbalancer.yaml b/.github/workflows/functional-loadbalancer.yaml index c1f48f556a..63300b1e76 100644 --- a/.github/workflows/functional-loadbalancer.yaml +++ b/.github/workflows/functional-loadbalancer.yaml @@ -46,7 +46,7 @@ jobs: enable_plugin neutron https://opendev.org/openstack/neutron ${{ matrix.openstack_version }} enabled_services: 'octavia,o-api,o-cw,o-hk,o-hm,o-da' - name: Checkout go - uses: actions/setup-go@v2 + uses: actions/setup-go@v3 with: go-version: '^1.15' - name: Run Gophercloud acceptance tests diff --git a/.github/workflows/functional-messaging.yaml b/.github/workflows/functional-messaging.yaml index 4588b37b54..db1471ef9a 100644 --- a/.github/workflows/functional-messaging.yaml +++ b/.github/workflows/functional-messaging.yaml @@ -44,7 +44,7 @@ jobs: conf_overrides: | enable_plugin zaqar https://opendev.org/openstack/zaqar ${{ matrix.openstack_version }} - name: Checkout go - uses: actions/setup-go@v2 + uses: actions/setup-go@v3 with: go-version: '^1.15' - name: Run Gophercloud acceptance tests diff --git a/.github/workflows/functional-networking.yaml b/.github/workflows/functional-networking.yaml index 887748778a..906bed37bc 100644 --- a/.github/workflows/functional-networking.yaml +++ b/.github/workflows/functional-networking.yaml @@ -56,7 +56,7 @@ jobs: ${{ matrix.devstack_conf_overrides }} enabled_services: 'neutron-dns,neutron-qos,neutron-segments,neutron-trunk,neutron-uplink-status-propagation,neutron-network-segment-range,neutron-port-forwarding' - name: Checkout go - uses: actions/setup-go@v2 + uses: actions/setup-go@v3 with: go-version: '^1.15' - name: Run Gophercloud acceptance tests diff --git a/.github/workflows/functional-objectstorage.yaml b/.github/workflows/functional-objectstorage.yaml index a4e6f5f2e9..3048980c2a 100644 --- a/.github/workflows/functional-objectstorage.yaml +++ b/.github/workflows/functional-objectstorage.yaml @@ -46,7 +46,7 @@ jobs: SWIFT_TEMPURL_KEY=secretkey enabled_services: 's-account,s-container,s-object,s-proxy' - name: Checkout go - uses: actions/setup-go@v2 + uses: actions/setup-go@v3 with: go-version: '^1.15' - name: Run Gophercloud acceptance tests diff --git a/.github/workflows/functional-orchestration.yaml b/.github/workflows/functional-orchestration.yaml index 27f371603b..05f6fb0ba3 100644 --- a/.github/workflows/functional-orchestration.yaml +++ b/.github/workflows/functional-orchestration.yaml @@ -45,7 +45,7 @@ jobs: enable_plugin heat https://opendev.org/openstack/heat ${{ matrix.openstack_version }} enabled_services: 'h-eng,h-api,h-api-cfn,h-api-cw' - name: Checkout go - uses: actions/setup-go@v2 + uses: actions/setup-go@v3 with: go-version: '^1.15' - name: Run Gophercloud acceptance tests diff --git a/.github/workflows/functional-placement.yaml b/.github/workflows/functional-placement.yaml index 98f8230d5c..b3b2f9b095 100644 --- a/.github/workflows/functional-placement.yaml +++ b/.github/workflows/functional-placement.yaml @@ -42,7 +42,7 @@ jobs: with: branch: ${{ matrix.openstack_version }} - name: Checkout go - uses: actions/setup-go@v2 + uses: actions/setup-go@v3 with: go-version: '^1.15' - name: Run Gophercloud acceptance tests diff --git a/.github/workflows/functional-sharedfilesystems.yaml b/.github/workflows/functional-sharedfilesystems.yaml index ef348cbd3c..2727878660 100644 --- a/.github/workflows/functional-sharedfilesystems.yaml +++ b/.github/workflows/functional-sharedfilesystems.yaml @@ -57,7 +57,7 @@ jobs: MANILA_DEFAULT_SHARE_TYPE_EXTRA_SPECS='snapshot_support=True create_share_from_snapshot_support=True revert_to_snapshot_support=True mount_snapshot_support=True' MANILA_CONFIGURE_DEFAULT_TYPES=True - name: Checkout go - uses: actions/setup-go@v2 + uses: actions/setup-go@v3 with: go-version: '^1.15' - name: Run Gophercloud acceptance tests diff --git a/.github/workflows/gomod.yml b/.github/workflows/gomod.yml index b96cd876c1..34530efbdc 100644 --- a/.github/workflows/gomod.yml +++ b/.github/workflows/gomod.yml @@ -8,7 +8,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - uses: actions/setup-go@v2 + - uses: actions/setup-go@v3 with: go-version: '1' - run: if [ $(go mod tidy && git diff | wc -l) -gt 0 ]; then git diff && exit 1; fi diff --git a/.github/workflows/reauth-retests.yaml b/.github/workflows/reauth-retests.yaml index 39130cc6ef..80e8cd06d2 100644 --- a/.github/workflows/reauth-retests.yaml +++ b/.github/workflows/reauth-retests.yaml @@ -14,7 +14,7 @@ jobs: steps: - name: Setup Go ${{ matrix.go-version }} - uses: actions/setup-go@v2 + uses: actions/setup-go@v3 with: go-version: ${{ matrix.go-version }} diff --git a/.github/workflows/unit.yml b/.github/workflows/unit.yml index 3c5deace5d..bca05da058 100644 --- a/.github/workflows/unit.yml +++ b/.github/workflows/unit.yml @@ -21,7 +21,7 @@ jobs: steps: - name: Setup Go ${{ matrix.go-version }} - uses: actions/setup-go@v2 + uses: actions/setup-go@v3 with: go-version: ${{ matrix.go-version }} From 1edc73e7afbf9447cf6bbc0f9d48e67681619bca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sandro=20J=C3=A4ckel?= <sandro.jaeckel@sap.com> Date: Fri, 11 Feb 2022 14:47:09 +0100 Subject: [PATCH 087/360] sharedfilesystems: Add support for `/v2/scheduler-stats/pools{,/details}` api endpoints --- .../v2/schedulerstats/doc.go | 22 ++ .../v2/schedulerstats/requests.go | 92 +++++++ .../v2/schedulerstats/results.go | 116 +++++++++ .../v2/schedulerstats/testing/fixtures.go | 241 ++++++++++++++++++ .../schedulerstats/testing/requests_test.go | 64 +++++ .../v2/schedulerstats/urls.go | 11 + 6 files changed, 546 insertions(+) create mode 100644 openstack/sharedfilesystems/v2/schedulerstats/doc.go create mode 100644 openstack/sharedfilesystems/v2/schedulerstats/requests.go create mode 100644 openstack/sharedfilesystems/v2/schedulerstats/results.go create mode 100644 openstack/sharedfilesystems/v2/schedulerstats/testing/fixtures.go create mode 100644 openstack/sharedfilesystems/v2/schedulerstats/testing/requests_test.go create mode 100644 openstack/sharedfilesystems/v2/schedulerstats/urls.go diff --git a/openstack/sharedfilesystems/v2/schedulerstats/doc.go b/openstack/sharedfilesystems/v2/schedulerstats/doc.go new file mode 100644 index 0000000000..f74c9836d0 --- /dev/null +++ b/openstack/sharedfilesystems/v2/schedulerstats/doc.go @@ -0,0 +1,22 @@ +/* +Package schedulerstats returns information about shared file systems capacity +and utilisation. Example: + + listOpts := schedulerstats.ListOpts{ + } + + allPages, err := schedulerstats.List(client, listOpts).AllPages() + if err != nil { + panic(err) + } + + allStats, err := schedulerstats.ExtractPools(allPages) + if err != nil { + panic(err) + } + + for _, stat := range allStats { + fmt.Printf("%+v\n", stat) + } +*/ +package schedulerstats diff --git a/openstack/sharedfilesystems/v2/schedulerstats/requests.go b/openstack/sharedfilesystems/v2/schedulerstats/requests.go new file mode 100644 index 0000000000..c325c58e45 --- /dev/null +++ b/openstack/sharedfilesystems/v2/schedulerstats/requests.go @@ -0,0 +1,92 @@ +package schedulerstats + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// ListOptsBuilder allows extensions to add additional parameters to the +// List request. +type ListOptsBuilder interface { + ToPoolsListQuery() (string, error) +} + +// ListOpts controls the view of data returned (e.g globally or per project). +type ListOpts struct { + // The pool name for the back end. + ProjectID string `json:"project_id,omitempty"` + // The pool name for the back end. + PoolName string `json:"pool_name"` + // The host name for the back end. + HostName string `json:"host_name"` + // The name of the back end. + BackendName string `json:"backend_name"` + // The capabilities for the storage back end. + Capabilities string `json:"capabilities"` + // The share type name or UUID. Allows filtering back end pools based on the extra-specs in the share type. + ShareType string `json:"share_type,omitempty"` +} + +// ToPoolsListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToPoolsListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// List makes a request against the API to list pool information. +func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := poolsListURL(client) + if opts != nil { + query, err := opts.ToPoolsListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return PoolPage{pagination.SinglePageBase(r)} + }) +} + +// ListDetailOptsBuilder allows extensions to add additional parameters to the +// ListDetail request. +type ListDetailOptsBuilder interface { + ToPoolsListQuery() (string, error) +} + +// ListOpts controls the view of data returned (e.g globally or per project). +type ListDetailOpts struct { + // The pool name for the back end. + ProjectID string `json:"project_id,omitempty"` + // The pool name for the back end. + PoolName string `json:"pool_name"` + // The host name for the back end. + HostName string `json:"host_name"` + // The name of the back end. + BackendName string `json:"backend_name"` + // The capabilities for the storage back end. + Capabilities string `json:"capabilities"` + // The share type name or UUID. Allows filtering back end pools based on the extra-specs in the share type. + ShareType string `json:"share_type,omitempty"` +} + +// ToPoolsListQuery formats a ListDetailOpts into a query string. +func (opts ListDetailOpts) ToPoolsListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// ListDetail makes a request against the API to list detailed pool information. +func ListDetail(client *gophercloud.ServiceClient, opts ListDetailOptsBuilder) pagination.Pager { + url := poolsListDetailURL(client) + if opts != nil { + query, err := opts.ToPoolsListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return PoolPage{pagination.SinglePageBase(r)} + }) +} diff --git a/openstack/sharedfilesystems/v2/schedulerstats/results.go b/openstack/sharedfilesystems/v2/schedulerstats/results.go new file mode 100644 index 0000000000..c3ef678b52 --- /dev/null +++ b/openstack/sharedfilesystems/v2/schedulerstats/results.go @@ -0,0 +1,116 @@ +package schedulerstats + +import ( + "encoding/json" + "math" + + "github.com/gophercloud/gophercloud/pagination" +) + +// Capabilities represents the information of an individual Pool. +type Capabilities struct { + // The following fields should be present in all storage drivers. + + // The quality of service (QoS) support. + Qos bool `json:"qos"` + // The date and time stamp when the API request was issued. + Timestamp string `json:"timestamp"` + // The name of the share back end. + ShareBackendName string `json:"share_backend_name"` + // Share server is usually a storage virtual machine or a lightweight container that is used to export shared file systems. + DriverHandlesShareServers bool `json:"driver_handles_share_servers"` + // The driver version of the back end. + DriverVersion string `json:"driver_version"` + // The amount of free capacity for the back end, in GiBs. A valid value is a string, such as unknown, or an integer. + FreeCapacityGB float64 `json:"-"` + // The storage protocol for the back end. For example, NFS_CIFS, glusterfs, HDFS, etc. + StorageProtocol string `json:"storage_protocol"` + // The total capacity for the back end, in GiBs. A valid value is a string, such as unknown, or an integer. + TotalCapacityGB float64 `json:"-"` + // The specification that filters back ends by whether they do or do not support share snapshots. + SnapshotSupport bool `json:"snapshot_support"` + // The back end replication domain. + ReplicationDomain string `json:"replication_domain"` + // The name of the vendor for the back end. + VendorName string `json:"vendor_name"` + + // The following fields are optional and may have empty values depending + + // on the storage driver in use. + ReservedPercentage int64 `json:"reserved_percentage"` + AllocatedCapacityGB float64 `json:"-"` +} + +// Pool represents an individual Pool retrieved from the +// schedulerstats API. +type Pool struct { + // The name of the back end. + Name string `json:"name"` + // The name of the back end. + Backend string `json:"backend"` + // The pool name for the back end. + Pool string `json:"pool"` + // The host name for the back end. + Host string `json:"host"` + // The back end capabilities which include qos, total_capacity_gb, etc. + Capabilities Capabilities `json:"capabilities,omitempty"` +} + +func (r *Capabilities) UnmarshalJSON(b []byte) error { + type tmp Capabilities + var s struct { + tmp + AllocatedCapacityGB interface{} `json:"allocated_capacity_gb"` + FreeCapacityGB interface{} `json:"free_capacity_gb"` + TotalCapacityGB interface{} `json:"total_capacity_gb"` + } + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + *r = Capabilities(s.tmp) + + // Generic function to parse a capacity value which may be a numeric + // value, "unknown", or "infinite" + parseCapacity := func(capacity interface{}) float64 { + if capacity != nil { + switch capacity.(type) { + case float64: + return capacity.(float64) + case string: + if capacity.(string) == "infinite" { + return math.Inf(1) + } + } + } + return 0.0 + } + + r.AllocatedCapacityGB = parseCapacity(s.AllocatedCapacityGB) + r.FreeCapacityGB = parseCapacity(s.FreeCapacityGB) + r.TotalCapacityGB = parseCapacity(s.TotalCapacityGB) + + return nil +} + +// PoolPage is a single page of all List results. +type PoolPage struct { + pagination.SinglePageBase +} + +// IsEmpty satisfies the IsEmpty method of the Page interface. It returns true +// if a List contains no results. +func (page PoolPage) IsEmpty() (bool, error) { + va, err := ExtractPools(page) + return len(va) == 0, err +} + +// ExtractPools takes a List result and extracts the collection of +// Pools returned by the API. +func ExtractPools(p pagination.Page) ([]Pool, error) { + var s struct { + Pools []Pool `json:"pools"` + } + err := (p.(PoolPage)).ExtractInto(&s) + return s.Pools, err +} diff --git a/openstack/sharedfilesystems/v2/schedulerstats/testing/fixtures.go b/openstack/sharedfilesystems/v2/schedulerstats/testing/fixtures.go new file mode 100644 index 0000000000..af89185486 --- /dev/null +++ b/openstack/sharedfilesystems/v2/schedulerstats/testing/fixtures.go @@ -0,0 +1,241 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/schedulerstats" + "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +const PoolsListBody = ` +{ + "pools": [ + { + "name": "opencloud@alpha#ALPHA_pool" + }, + { + "name": "opencloud@beta#BETA_pool" + }, + { + "name": "opencloud@gamma#GAMMA_pool" + }, + { + "name": "opencloud@delta#DELTA_pool" + } + ] +} +` + +const PoolsListBodyDetail = ` +{ + "pools": [ + { + "name": "opencloud@alpha#ALPHA_pool", + "host": "opencloud", + "backend": "alpha", + "pool": "ALPHA_pool", + "capabilities": { + "pool_name": "ALPHA_pool", + "total_capacity_gb": 1230.0, + "free_capacity_gb": 1210.0, + "reserved_percentage": 0, + "share_backend_name": "ALPHA", + "storage_protocol": "NFS_CIFS", + "vendor_name": "Open Source", + "driver_version": "1.0", + "timestamp": "2019-05-07T00:28:02.935569", + "driver_handles_share_servers": true, + "snapshot_support": true, + "create_share_from_snapshot_support": true, + "revert_to_snapshot_support": true, + "mount_snapshot_support": true, + "dedupe": false, + "compression": false, + "replication_type": null, + "replication_domain": null, + "sg_consistent_snapshot_support": "pool", + "ipv4_support": true, + "ipv6_support": false + } + }, + { + "name": "opencloud@beta#BETA_pool", + "host": "opencloud", + "backend": "beta", + "pool": "BETA_pool", + "capabilities": { + "pool_name": "BETA_pool", + "total_capacity_gb": 1230.0, + "free_capacity_gb": 1210.0, + "reserved_percentage": 0, + "share_backend_name": "BETA", + "storage_protocol": "NFS_CIFS", + "vendor_name": "Open Source", + "driver_version": "1.0", + "timestamp": "2019-05-07T00:28:02.817309", + "driver_handles_share_servers": true, + "snapshot_support": true, + "create_share_from_snapshot_support": true, + "revert_to_snapshot_support": true, + "mount_snapshot_support": true, + "dedupe": false, + "compression": false, + "replication_type": null, + "replication_domain": null, + "sg_consistent_snapshot_support": "pool", + "ipv4_support": true, + "ipv6_support": false + } + }, + { + "name": "opencloud@gamma#GAMMA_pool", + "host": "opencloud", + "backend": "gamma", + "pool": "GAMMA_pool", + "capabilities": { + "pool_name": "GAMMA_pool", + "total_capacity_gb": 1230.0, + "free_capacity_gb": 1210.0, + "reserved_percentage": 0, + "replication_type": "readable", + "share_backend_name": "GAMMA", + "storage_protocol": "NFS_CIFS", + "vendor_name": "Open Source", + "driver_version": "1.0", + "timestamp": "2019-05-07T00:28:02.899888", + "driver_handles_share_servers": false, + "snapshot_support": true, + "create_share_from_snapshot_support": true, + "revert_to_snapshot_support": true, + "mount_snapshot_support": true, + "dedupe": false, + "compression": false, + "replication_domain": "replica_domain_store1", + "sg_consistent_snapshot_support": "pool", + "ipv4_support": true, + "ipv6_support": false + } + }, + { + "name": "opencloud@delta#DELTA_pool", + "host": "opencloud", + "backend": "delta", + "pool": "DELTA_pool", + "capabilities": { + "pool_name": "DELTA_pool", + "total_capacity_gb": 1230.0, + "free_capacity_gb": 1210.0, + "reserved_percentage": 0, + "replication_type": "readable", + "share_backend_name": "DELTA", + "storage_protocol": "NFS_CIFS", + "vendor_name": "Open Source", + "driver_version": "1.0", + "timestamp": "2019-05-07T00:28:02.963660", + "driver_handles_share_servers": false, + "snapshot_support": true, + "create_share_from_snapshot_support": true, + "revert_to_snapshot_support": true, + "mount_snapshot_support": true, + "dedupe": false, + "compression": false, + "replication_domain": "replica_domain_store1", + "sg_consistent_snapshot_support": "pool", + "ipv4_support": true, + "ipv6_support": false + } + } + ] +} +` + +var ( + PoolFake1 = schedulerstats.Pool{ + Name: "opencloud@alpha#ALPHA_pool", + } + + PoolFake2 = schedulerstats.Pool{ + Name: "opencloud@beta#BETA_pool", + } + + PoolFake3 = schedulerstats.Pool{ + Name: "opencloud@gamma#GAMMA_pool", + } + + PoolFake4 = schedulerstats.Pool{ + Name: "opencloud@delta#DELTA_pool", + } + + PoolDetailFake1 = schedulerstats.Pool{ + Name: "opencloud@alpha#ALPHA_pool", + Host: "opencloud", + Capabilities: schedulerstats.Capabilities{ + DriverVersion: "1.0", + FreeCapacityGB: 1210, + StorageProtocol: "NFS_CIFS", + TotalCapacityGB: 1230, + VendorName: "Open Source", + }, + } + + PoolDetailFake2 = schedulerstats.Pool{ + Name: "opencloud@beta#BETA_pool", + Host: "opencloud", + Capabilities: schedulerstats.Capabilities{ + DriverVersion: "1.0", + FreeCapacityGB: 1210, + StorageProtocol: "NFS_CIFS", + TotalCapacityGB: 1230, + VendorName: "Open Source", + }, + } + + PoolDetailFake3 = schedulerstats.Pool{ + Name: "opencloud@gamma#GAMMA_pool", + Host: "opencloud", + Capabilities: schedulerstats.Capabilities{ + DriverVersion: "1.0", + FreeCapacityGB: 1210, + StorageProtocol: "NFS_CIFS", + TotalCapacityGB: 1230, + VendorName: "Open Source", + }, + } + + PoolDetailFake4 = schedulerstats.Pool{ + Name: "opencloud@delta#DELTA_pool", + Host: "opencloud", + Capabilities: schedulerstats.Capabilities{ + DriverVersion: "1.0", + FreeCapacityGB: 1210, + StorageProtocol: "NFS_CIFS", + TotalCapacityGB: 1230, + VendorName: "Open Source", + }, + } +) + +func HandlePoolsListSuccessfully(t *testing.T) { + testhelper.Mux.HandleFunc("/scheduler-stats/pools", func(w http.ResponseWriter, r *http.Request) { + testhelper.TestMethod(t, r, "GET") + testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + w.Header().Add("Content-Type", "application/json") + + r.ParseForm() + fmt.Fprintf(w, PoolsListBody) + + }) + testhelper.Mux.HandleFunc("/scheduler-stats/pools/detail", func(w http.ResponseWriter, r *http.Request) { + testhelper.TestMethod(t, r, "GET") + testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + w.Header().Add("Content-Type", "application/json") + + r.ParseForm() + fmt.Fprintf(w, PoolsListBodyDetail) + }) +} diff --git a/openstack/sharedfilesystems/v2/schedulerstats/testing/requests_test.go b/openstack/sharedfilesystems/v2/schedulerstats/testing/requests_test.go new file mode 100644 index 0000000000..cd1df62cce --- /dev/null +++ b/openstack/sharedfilesystems/v2/schedulerstats/testing/requests_test.go @@ -0,0 +1,64 @@ +package testing + +import ( + "testing" + + "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/schedulerstats" + "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestListPoolsDetail(t *testing.T) { + testhelper.SetupHTTP() + defer testhelper.TeardownHTTP() + HandlePoolsListSuccessfully(t) + + pages := 0 + err := schedulerstats.List(client.ServiceClient(), schedulerstats.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + pages++ + + actual, err := schedulerstats.ExtractPools(page) + testhelper.AssertNoErr(t, err) + + if len(actual) != 4 { + t.Fatalf("Expected 4 backends, got %d", len(actual)) + } + testhelper.CheckDeepEquals(t, PoolFake1, actual[0]) + testhelper.CheckDeepEquals(t, PoolFake2, actual[1]) + testhelper.CheckDeepEquals(t, PoolFake3, actual[2]) + testhelper.CheckDeepEquals(t, PoolFake4, actual[3]) + + return true, nil + }) + + testhelper.AssertNoErr(t, err) + + if pages != 1 { + t.Errorf("Expected 1 page, saw %d", pages) + } + + pages = 0 + err = schedulerstats.ListDetail(client.ServiceClient(), schedulerstats.ListDetailOpts{}).EachPage(func(page pagination.Page) (bool, error) { + pages++ + + actual, err := schedulerstats.ExtractPools(page) + testhelper.AssertNoErr(t, err) + + if len(actual) != 4 { + t.Fatalf("Expected 4 backends, got %d", len(actual)) + } + testhelper.CheckDeepEquals(t, PoolDetailFake1, actual[0]) + testhelper.CheckDeepEquals(t, PoolDetailFake2, actual[1]) + testhelper.CheckDeepEquals(t, PoolDetailFake3, actual[2]) + testhelper.CheckDeepEquals(t, PoolDetailFake4, actual[3]) + + return true, nil + }) + + testhelper.AssertNoErr(t, err) + + if pages != 1 { + t.Errorf("Expected 1 page, saw %d", pages) + } +} diff --git a/openstack/sharedfilesystems/v2/schedulerstats/urls.go b/openstack/sharedfilesystems/v2/schedulerstats/urls.go new file mode 100644 index 0000000000..1c907ffd1d --- /dev/null +++ b/openstack/sharedfilesystems/v2/schedulerstats/urls.go @@ -0,0 +1,11 @@ +package schedulerstats + +import "github.com/gophercloud/gophercloud" + +func poolsListURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("scheduler-stats", "pools") +} + +func poolsListDetailURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("scheduler-stats", "pools", "detail") +} From 420633e0e5a800fad1671f7ae415d4e2f50e7580 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sandro=20J=C3=A4ckel?= <sandro.jaeckel@sap.com> Date: Thu, 24 Feb 2022 17:13:34 +0100 Subject: [PATCH 088/360] Add basic acceptance tests for manila/v2/{services,schedulerstats} --- .../v2/schedulerstats_test.go | 29 +++++ .../sharedfilesystems/v2/services_test.go | 32 +++++ .../v2/schedulerstats/testing/fixtures.go | 122 ++++++++++++------ 3 files changed, 145 insertions(+), 38 deletions(-) create mode 100644 acceptance/openstack/sharedfilesystems/v2/schedulerstats_test.go create mode 100644 acceptance/openstack/sharedfilesystems/v2/services_test.go diff --git a/acceptance/openstack/sharedfilesystems/v2/schedulerstats_test.go b/acceptance/openstack/sharedfilesystems/v2/schedulerstats_test.go new file mode 100644 index 0000000000..fc2d17e5e8 --- /dev/null +++ b/acceptance/openstack/sharedfilesystems/v2/schedulerstats_test.go @@ -0,0 +1,29 @@ +//go:build acceptance +// +build acceptance + +package v2 + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/schedulerstats" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestSchedulerStatsList(t *testing.T) { + client, err := clients.NewSharedFileSystemV2Client() + client.Microversion = "2.23" + th.AssertNoErr(t, err) + + allPages, err := schedulerstats.List(client, nil).AllPages() + th.AssertNoErr(t, err) + + allPools, err := schedulerstats.ExtractPools(allPages) + th.AssertNoErr(t, err) + + for _, recordset := range allPools { + tools.PrintResource(t, &recordset) + } +} diff --git a/acceptance/openstack/sharedfilesystems/v2/services_test.go b/acceptance/openstack/sharedfilesystems/v2/services_test.go new file mode 100644 index 0000000000..b1340e8752 --- /dev/null +++ b/acceptance/openstack/sharedfilesystems/v2/services_test.go @@ -0,0 +1,32 @@ +//go:build acceptance +// +build acceptance + +package v2 + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/services" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestServicesList(t *testing.T) { + client, err := clients.NewSharedFileSystemV2Client() + th.AssertNoErr(t, err) + + client.Microversion = "2.7" + allPages, err := services.List(client, nil).AllPages() + th.AssertNoErr(t, err) + + allServices, err := services.ExtractServices(allPages) + th.AssertNoErr(t, err) + + th.AssertIntGreaterOrEqual(t, len(allServices), 1) + + for _, s := range allServices { + tools.PrintResource(t, &s) + th.AssertEquals(t, s.Status, "enabled") + } +} diff --git a/openstack/sharedfilesystems/v2/schedulerstats/testing/fixtures.go b/openstack/sharedfilesystems/v2/schedulerstats/testing/fixtures.go index af89185486..244e068b94 100644 --- a/openstack/sharedfilesystems/v2/schedulerstats/testing/fixtures.go +++ b/openstack/sharedfilesystems/v2/schedulerstats/testing/fixtures.go @@ -14,16 +14,28 @@ const PoolsListBody = ` { "pools": [ { - "name": "opencloud@alpha#ALPHA_pool" + "name": "opencloud@alpha#ALPHA_pool", + "host": "opencloud", + "backend": "alpha", + "pool": "ALPHA_pool" }, { - "name": "opencloud@beta#BETA_pool" + "name": "opencloud@beta#BETA_pool", + "host": "opencloud", + "backend": "beta", + "pool": "BETA_pool" }, { - "name": "opencloud@gamma#GAMMA_pool" + "name": "opencloud@gamma#GAMMA_pool", + "host": "opencloud", + "backend": "gamma", + "pool": "GAMMA_pool" }, { - "name": "opencloud@delta#DELTA_pool" + "name": "opencloud@delta#DELTA_pool", + "host": "opencloud", + "backend": "delta", + "pool": "DELTA_pool" } ] } @@ -113,7 +125,6 @@ const PoolsListBodyDetail = ` "mount_snapshot_support": true, "dedupe": false, "compression": false, - "replication_domain": "replica_domain_store1", "sg_consistent_snapshot_support": "pool", "ipv4_support": true, "ipv6_support": false @@ -142,7 +153,6 @@ const PoolsListBodyDetail = ` "mount_snapshot_support": true, "dedupe": false, "compression": false, - "replication_domain": "replica_domain_store1", "sg_consistent_snapshot_support": "pool", "ipv4_support": true, "ipv6_support": false @@ -154,66 +164,102 @@ const PoolsListBodyDetail = ` var ( PoolFake1 = schedulerstats.Pool{ - Name: "opencloud@alpha#ALPHA_pool", + Name: "opencloud@alpha#ALPHA_pool", + Host: "opencloud", + Backend: "alpha", + Pool: "ALPHA_pool", } PoolFake2 = schedulerstats.Pool{ - Name: "opencloud@beta#BETA_pool", + Name: "opencloud@beta#BETA_pool", + Host: "opencloud", + Backend: "beta", + Pool: "BETA_pool", } PoolFake3 = schedulerstats.Pool{ - Name: "opencloud@gamma#GAMMA_pool", + Name: "opencloud@gamma#GAMMA_pool", + Host: "opencloud", + Backend: "gamma", + Pool: "GAMMA_pool", } PoolFake4 = schedulerstats.Pool{ - Name: "opencloud@delta#DELTA_pool", + Name: "opencloud@delta#DELTA_pool", + Host: "opencloud", + Backend: "delta", + Pool: "DELTA_pool", } PoolDetailFake1 = schedulerstats.Pool{ - Name: "opencloud@alpha#ALPHA_pool", - Host: "opencloud", + Name: "opencloud@alpha#ALPHA_pool", + Host: "opencloud", + Backend: "alpha", + Pool: "ALPHA_pool", Capabilities: schedulerstats.Capabilities{ - DriverVersion: "1.0", - FreeCapacityGB: 1210, - StorageProtocol: "NFS_CIFS", - TotalCapacityGB: 1230, - VendorName: "Open Source", + DriverVersion: "1.0", + FreeCapacityGB: 1210, + StorageProtocol: "NFS_CIFS", + TotalCapacityGB: 1230, + VendorName: "Open Source", + ShareBackendName: "ALPHA", + Timestamp: "2019-05-07T00:28:02.935569", + DriverHandlesShareServers: true, + SnapshotSupport: true, }, } PoolDetailFake2 = schedulerstats.Pool{ - Name: "opencloud@beta#BETA_pool", - Host: "opencloud", + Name: "opencloud@beta#BETA_pool", + Host: "opencloud", + Backend: "beta", + Pool: "BETA_pool", Capabilities: schedulerstats.Capabilities{ - DriverVersion: "1.0", - FreeCapacityGB: 1210, - StorageProtocol: "NFS_CIFS", - TotalCapacityGB: 1230, - VendorName: "Open Source", + DriverVersion: "1.0", + FreeCapacityGB: 1210, + StorageProtocol: "NFS_CIFS", + TotalCapacityGB: 1230, + VendorName: "Open Source", + ShareBackendName: "BETA", + Timestamp: "2019-05-07T00:28:02.817309", + DriverHandlesShareServers: true, + SnapshotSupport: true, }, } PoolDetailFake3 = schedulerstats.Pool{ - Name: "opencloud@gamma#GAMMA_pool", - Host: "opencloud", + Name: "opencloud@gamma#GAMMA_pool", + Host: "opencloud", + Backend: "gamma", + Pool: "GAMMA_pool", Capabilities: schedulerstats.Capabilities{ - DriverVersion: "1.0", - FreeCapacityGB: 1210, - StorageProtocol: "NFS_CIFS", - TotalCapacityGB: 1230, - VendorName: "Open Source", + DriverVersion: "1.0", + FreeCapacityGB: 1210, + StorageProtocol: "NFS_CIFS", + TotalCapacityGB: 1230, + VendorName: "Open Source", + ShareBackendName: "GAMMA", + Timestamp: "2019-05-07T00:28:02.899888", + DriverHandlesShareServers: false, + SnapshotSupport: true, }, } PoolDetailFake4 = schedulerstats.Pool{ - Name: "opencloud@delta#DELTA_pool", - Host: "opencloud", + Name: "opencloud@delta#DELTA_pool", + Host: "opencloud", + Backend: "delta", + Pool: "DELTA_pool", Capabilities: schedulerstats.Capabilities{ - DriverVersion: "1.0", - FreeCapacityGB: 1210, - StorageProtocol: "NFS_CIFS", - TotalCapacityGB: 1230, - VendorName: "Open Source", + DriverVersion: "1.0", + FreeCapacityGB: 1210, + StorageProtocol: "NFS_CIFS", + TotalCapacityGB: 1230, + VendorName: "Open Source", + ShareBackendName: "DELTA", + Timestamp: "2019-05-07T00:28:02.963660", + DriverHandlesShareServers: false, + SnapshotSupport: true, }, } ) From 43edb96ac8222503583ef0f7b68fe6cc753ed1d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sandro=20J=C3=A4ckel?= <sandro.jaeckel@sap.com> Date: Thu, 31 Mar 2022 16:00:23 +0200 Subject: [PATCH 089/360] Fix typo --- acceptance/openstack/sharedfilesystems/v2/shares.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acceptance/openstack/sharedfilesystems/v2/shares.go b/acceptance/openstack/sharedfilesystems/v2/shares.go index f5d4141005..33c1c3ac19 100644 --- a/acceptance/openstack/sharedfilesystems/v2/shares.go +++ b/acceptance/openstack/sharedfilesystems/v2/shares.go @@ -16,7 +16,7 @@ import ( // error will be returned if the share could not be created func CreateShare(t *testing.T, client *gophercloud.ServiceClient) (*shares.Share, error) { if testing.Short() { - t.Skip("Skipping test that requres share creation in short mode.") + t.Skip("Skipping test that requires share creation in short mode.") } iTrue := true From 01353431d9b11307b19f0d89c55b530ad19c6084 Mon Sep 17 00:00:00 2001 From: shhgs <shhgs@yahoo.com> Date: Fri, 6 May 2022 21:26:54 -0400 Subject: [PATCH 090/360] Neutron v2: BGP Speaker create / delete --- .../bgp/speakers/bgpspeakers_test.go | 76 +++++++++++++++++++ .../v2/extensions/bgp/speakers/doc.go | 2 + .../v2/extensions/bgp/speakers/doc.go | 30 ++++++++ .../v2/extensions/bgp/speakers/requests.go | 40 ++++++++++ .../v2/extensions/bgp/speakers/results.go | 12 +++ .../bgp/speakers/testing/fixture.go | 28 +++++++ .../bgp/speakers/testing/requests_test.go | 51 +++++++++++++ .../v2/extensions/bgp/speakers/urls.go | 10 +++ 8 files changed, 249 insertions(+) create mode 100644 acceptance/openstack/networking/v2/extensions/bgp/speakers/bgpspeakers_test.go create mode 100644 acceptance/openstack/networking/v2/extensions/bgp/speakers/doc.go diff --git a/acceptance/openstack/networking/v2/extensions/bgp/speakers/bgpspeakers_test.go b/acceptance/openstack/networking/v2/extensions/bgp/speakers/bgpspeakers_test.go new file mode 100644 index 0000000000..50caca7390 --- /dev/null +++ b/acceptance/openstack/networking/v2/extensions/bgp/speakers/bgpspeakers_test.go @@ -0,0 +1,76 @@ +package speakers + +import ( + "strconv" + "testing" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/bgp/speakers" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func CreateBGPSpeaker(t *testing.T, client *gophercloud.ServiceClient) (*speakers.BGPSpeaker, error) { + opts := speakers.CreateOpts{ + IPVersion: 4, + AdvertiseFloatingIPHostRoutes: false, + AdvertiseTenantNetworks: true, + Name: tools.RandomString("TESTACC-BGPSPEAKER-", 8), + LocalAS: "3000", + Networks: []string{}, + } + + t.Logf("Attempting to create BGP Speaker: %s", opts.Name) + bgpSpeaker, err := speakers.Create(client, opts).Extract() + if err != nil { + return bgpSpeaker, err + } + + localas, err := strconv.Atoi(opts.LocalAS) + t.Logf("Successfully created BGP Speaker") + th.AssertEquals(t, bgpSpeaker.Name, opts.Name) + th.AssertEquals(t, bgpSpeaker.LocalAS, localas) + th.AssertEquals(t, bgpSpeaker.IPVersion, opts.IPVersion) + th.AssertEquals(t, bgpSpeaker.AdvertiseTenantNetworks, opts.AdvertiseTenantNetworks) + th.AssertEquals(t, bgpSpeaker.AdvertiseFloatingIPHostRoutes, opts.AdvertiseFloatingIPHostRoutes) + return bgpSpeaker, err +} + +func TestBGPSpeakerCRD(t *testing.T) { + clients.RequireAdmin(t) + + client, err := clients.NewNetworkV2Client() + th.AssertNoErr(t, err) + + // Create a BGP Speaker + bgpSpeakerCreated, err := CreateBGPSpeaker(t, client) + th.AssertNoErr(t, err) + tools.PrintResource(t, bgpSpeakerCreated) + + // Get a BGP Speaker + bgpSpeakerGot, err := speakers.Get(client, bgpSpeakerCreated.ID).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, bgpSpeakerCreated.ID, bgpSpeakerGot.ID) + th.AssertEquals(t, bgpSpeakerCreated.Name, bgpSpeakerGot.Name) + + // List BGP Speakers + allPages, err := speakers.List(client).AllPages() + th.AssertNoErr(t, err) + allSpeakers, err := speakers.ExtractBGPSpeakers(allPages) + th.AssertNoErr(t, err) + + t.Logf("Retrieved BGP Speakers") + tools.PrintResource(t, allSpeakers) + th.AssertIntGreaterOrEqual(t, len(allSpeakers), 1) + + // Delete a BGP Speaker + t.Logf("Attempting to delete BGP Speaker: %s", bgpSpeakerGot.Name) + err = speakers.Delete(client, bgpSpeakerGot.ID).ExtractErr() + th.AssertNoErr(t, err) + + // Confirm the BGP Speaker is deleted + bgpSpeakerGot, err = speakers.Get(client, bgpSpeakerGot.ID).Extract() + th.AssertErr(t, err) + t.Logf("BGP Speaker %s deleted", bgpSpeakerCreated.Name) +} diff --git a/acceptance/openstack/networking/v2/extensions/bgp/speakers/doc.go b/acceptance/openstack/networking/v2/extensions/bgp/speakers/doc.go new file mode 100644 index 0000000000..9e3a7d2f14 --- /dev/null +++ b/acceptance/openstack/networking/v2/extensions/bgp/speakers/doc.go @@ -0,0 +1,2 @@ +// BGP Peer acceptance tests +package speakers diff --git a/openstack/networking/v2/extensions/bgp/speakers/doc.go b/openstack/networking/v2/extensions/bgp/speakers/doc.go index 23f21e35a9..36dfb2cad6 100644 --- a/openstack/networking/v2/extensions/bgp/speakers/doc.go +++ b/openstack/networking/v2/extensions/bgp/speakers/doc.go @@ -31,4 +31,34 @@ Example: log.Panic(nil) } log.Printf("%+v", *speaker) + + +3. Create BGP Speaker, a.k.a. POST /bgp-speakers + +Example: + + opts := speakers.CreateOpts{ + IPVersion: 6, + AdvertiseFloatingIPHostRoutes: false, + AdvertiseTenantNetworks: true, + Name: "gophercloud-testing-bgp-speaker", + LocalAS: "2000", + Networks: []string{}, + } + r, err := speaker.Create(c, opts).Extract() + if err != nil { + log.Panic(err) + } + log.Printf("%+v", *r) + + +5. Delete BGP Speaker, a.k.a. DELETE /bgp-speakers/{id} + +Example: + + err := speaker.Delete(auth, speakerID).ExtractErr() + if err != nil { + log.Panic(err) + } + log.Printf("Speaker Deleted") */ diff --git a/openstack/networking/v2/extensions/bgp/speakers/requests.go b/openstack/networking/v2/extensions/bgp/speakers/requests.go index 58947b3e8b..65c0b2f267 100644 --- a/openstack/networking/v2/extensions/bgp/speakers/requests.go +++ b/openstack/networking/v2/extensions/bgp/speakers/requests.go @@ -19,3 +19,43 @@ func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } + +// CreateOpts represents options used to create a network. +type CreateOpts struct { + Name string `json:"name"` + IPVersion int `json:"ip_version"` + AdvertiseFloatingIPHostRoutes bool `json:"advertise_floating_ip_host_routes"` + AdvertiseTenantNetworks bool `json:"advertise_tenant_networks"` + LocalAS string `json:"local_as"` + Networks []string `json:"networks,omitempty"` +} + +// CreateOptsBuilder allows extensions to add additional parameters to the +// Create request. +type CreateOptsBuilder interface { + ToSpeakerCreateMap() (map[string]interface{}, error) +} + +// ToSpeakerCreateMap builds a request body from CreateOpts. +func (opts CreateOpts) ToSpeakerCreateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, jroot) +} + +// Create accepts a CreateOpts and create a BGP Speaker. +func Create(c *gophercloud.ServiceClient, opts CreateOpts) (r CreateResult) { + b, err := opts.ToSpeakerCreateMap() + if err != nil { + r.Err = err + return + } + resp, err := c.Post(createURL(c), b, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// Delete accepts a unique ID and deletes the bgp speaker associated with it. +func Delete(c *gophercloud.ServiceClient, speakerID string) (r DeleteResult) { + resp, err := c.Delete(deleteURL(c, speakerID), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} diff --git a/openstack/networking/v2/extensions/bgp/speakers/results.go b/openstack/networking/v2/extensions/bgp/speakers/results.go index 1bd5f68f59..c0dd78fb5c 100644 --- a/openstack/networking/v2/extensions/bgp/speakers/results.go +++ b/openstack/networking/v2/extensions/bgp/speakers/results.go @@ -85,3 +85,15 @@ func ExtractBGPSpeakersInto(r pagination.Page, v interface{}) error { type GetResult struct { commonResult } + +// CreateResult represents the result of a create operation. Call its Extract +// method to interpret it as a BGPSpeaker. +type CreateResult struct { + commonResult +} + +// DeleteResult represents the result of a delete operation. Call its +// ExtractErr method to determine if the request succeeded or failed. +type DeleteResult struct { + gophercloud.ErrResult +} diff --git a/openstack/networking/v2/extensions/bgp/speakers/testing/fixture.go b/openstack/networking/v2/extensions/bgp/speakers/testing/fixture.go index 137682e9bf..85d3a3d403 100644 --- a/openstack/networking/v2/extensions/bgp/speakers/testing/fixture.go +++ b/openstack/networking/v2/extensions/bgp/speakers/testing/fixture.go @@ -61,3 +61,31 @@ const GetBGPSpeakerResult = ` } } ` +const CreateRequest = ` +{ + "bgp_speaker": { + "advertise_floating_ip_host_routes": false, + "advertise_tenant_networks": true, + "ip_version": 6, + "local_as": "2000", + "name": "gophercloud-testing-bgp-speaker" + } +} +` + +const CreateResponse = ` +{ + "bgp_speaker": { + "peers": [], + "project_id": "7fa3f96b-17ee-4d1b-8fbf-fe889bb1f1d0", + "name": "gophercloud-testing-bgp-speaker", + "tenant_id": "7fa3f96b-17ee-4d1b-8fbf-fe889bb1f1d0", + "local_as": 2000, + "advertise_tenant_networks": true, + "networks": [], + "ip_version": 6, + "advertise_floating_ip_host_routes": false, + "id": "26e98af2-4dc7-452a-91b0-65ee45f3e7c1" + } +} +` diff --git a/openstack/networking/v2/extensions/bgp/speakers/testing/requests_test.go b/openstack/networking/v2/extensions/bgp/speakers/testing/requests_test.go index 4f257302c7..e35b5dce31 100644 --- a/openstack/networking/v2/extensions/bgp/speakers/testing/requests_test.go +++ b/openstack/networking/v2/extensions/bgp/speakers/testing/requests_test.go @@ -58,3 +58,54 @@ func TestGet(t *testing.T) { th.AssertNoErr(t, err) th.CheckDeepEquals(t, *s, BGPSpeaker1) } + +func TestCreate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/bgp-speakers", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestHeader(t, r, "Content-Type", "application/json") + th.TestHeader(t, r, "Accept", "application/json") + th.TestJSONRequest(t, r, CreateRequest) + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusCreated) + fmt.Fprintf(w, CreateResponse) + }) + + opts := speakers.CreateOpts{ + IPVersion: 6, + AdvertiseFloatingIPHostRoutes: false, + AdvertiseTenantNetworks: true, + Name: "gophercloud-testing-bgp-speaker", + LocalAS: "2000", + Networks: []string{}, + } + r, err := speakers.Create(fake.ServiceClient(), opts).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, r.Name, opts.Name) + th.AssertEquals(t, r.LocalAS, 2000) + th.AssertEquals(t, len(r.Networks), 0) + th.AssertEquals(t, r.IPVersion, opts.IPVersion) + th.AssertEquals(t, r.AdvertiseFloatingIPHostRoutes, opts.AdvertiseFloatingIPHostRoutes) + th.AssertEquals(t, r.AdvertiseTenantNetworks, opts.AdvertiseTenantNetworks) +} + +func TestDelete(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + bgpSpeakerID := "ab01ade1-ae62-43c9-8a1f-3c24225b96d8" + th.Mux.HandleFunc("/v2.0/bgp-speakers/"+bgpSpeakerID, func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "DELETE") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestHeader(t, r, "Accept", "application/json") + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusNoContent) + }) + + err := speakers.Delete(fake.ServiceClient(), bgpSpeakerID).ExtractErr() + th.AssertNoErr(t, err) +} diff --git a/openstack/networking/v2/extensions/bgp/speakers/urls.go b/openstack/networking/v2/extensions/bgp/speakers/urls.go index 06b636fdb8..bf579dc8fe 100644 --- a/openstack/networking/v2/extensions/bgp/speakers/urls.go +++ b/openstack/networking/v2/extensions/bgp/speakers/urls.go @@ -23,3 +23,13 @@ func getURL(c *gophercloud.ServiceClient, id string) string { func listURL(c *gophercloud.ServiceClient) string { return rootURL(c) } + +// return /v2.0/bgp-speakers +func createURL(c *gophercloud.ServiceClient) string { + return rootURL(c) +} + +// return /v2.0/bgp-speakers/{bgp-peer-id} +func deleteURL(c *gophercloud.ServiceClient, id string) string { + return resourceURL(c, id) +} From bdb0c7ffc21748da362c93e3592f14b4dc821ff8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= <m.andre@redhat.com> Date: Fri, 8 Apr 2022 14:27:09 +0200 Subject: [PATCH 091/360] [CI-v2] Add containerinfra job with Github Action - Deploy Magnum with Devstack - Run messaging acceptance tests - Logs failures if any --- .../workflows/functional-containerinfra.yaml | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 .github/workflows/functional-containerinfra.yaml diff --git a/.github/workflows/functional-containerinfra.yaml b/.github/workflows/functional-containerinfra.yaml new file mode 100644 index 0000000000..52eb34ab34 --- /dev/null +++ b/.github/workflows/functional-containerinfra.yaml @@ -0,0 +1,62 @@ +name: functional-containerinfra +on: + pull_request: + paths: + - '**containerinfra**' + schedule: + - cron: '0 0 * * *' +jobs: + functional-containerinfra: + strategy: + fail-fast: false + matrix: + name: ["master"] + openstack_version: ["master"] + ubuntu_version: ["20.04"] + include: + - name: "xena" + openstack_version: "stable/xena" + ubuntu_version: "20.04" + - name: "wallaby" + openstack_version: "stable/wallaby" + ubuntu_version: "20.04" + - name: "victoria" + openstack_version: "stable/victoria" + ubuntu_version: "20.04" + - name: "ussuri" + openstack_version: "stable/ussuri" + ubuntu_version: "18.04" + - name: "train" + openstack_version: "stable/train" + ubuntu_version: "18.04" + runs-on: ubuntu-${{ matrix.ubuntu_version }} + name: Deploy OpenStack ${{ matrix.name }} with Magnum and run containerinfra acceptance tests + steps: + - name: Checkout Gophercloud + uses: actions/checkout@v2 + - name: Deploy devstack + uses: EmilienM/devstack-action@v0.6 + with: + branch: ${{ matrix.openstack_version }} + conf_overrides: | + enable_plugin heat https://opendev.org/openstack/heat ${{ matrix.openstack_version }} + enable_plugin magnum https://opendev.org/openstack/magnum ${{ matrix.openstack_version }} + enabled_services: 'h-eng,h-api,h-api-cfn,h-api-cw' + - name: Checkout go + uses: actions/setup-go@v2 + with: + go-version: '^1.15' + - name: Run Gophercloud acceptance tests + run: ./script/acceptancetest + env: + DEVSTACK_PATH: ${{ github.workspace }}/devstack + ACCEPTANCE_TESTS_FILTER: "^.*containerinfra.*$" + - name: Generate logs on failure + run: ./script/collectlogs + if: failure() + - name: Upload logs artifacts on failure + if: failure() + uses: actions/upload-artifact@v2 + with: + name: functional-containerinfra-${{ matrix.name }} + path: /tmp/devstack-logs/* From 231664b7e103a1df158432c9b1456d71dec261d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= <m.andre@redhat.com> Date: Fri, 8 Apr 2022 16:20:12 +0200 Subject: [PATCH 092/360] Bump Glance quota when installing with Magnum The default image size quota is 1GiB and it too small for the Fedora CoreOS image needed for Magnum. Bump the image quota to 5GiB. Also bump swift max size accordingly. --- .github/workflows/functional-containerinfra.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/functional-containerinfra.yaml b/.github/workflows/functional-containerinfra.yaml index 52eb34ab34..f3062a54ea 100644 --- a/.github/workflows/functional-containerinfra.yaml +++ b/.github/workflows/functional-containerinfra.yaml @@ -41,6 +41,8 @@ jobs: conf_overrides: | enable_plugin heat https://opendev.org/openstack/heat ${{ matrix.openstack_version }} enable_plugin magnum https://opendev.org/openstack/magnum ${{ matrix.openstack_version }} + GLANCE_LIMIT_IMAGE_SIZE_TOTAL=5000 + SWIFT_MAX_FILE_SIZE=5368709122 enabled_services: 'h-eng,h-api,h-api-cfn,h-api-cw' - name: Checkout go uses: actions/setup-go@v2 From 498ac3fb5b00df6af28b6992eb1a967ab7cce3a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= <martin.andre@gmail.com> Date: Thu, 30 Dec 2021 19:11:51 +0100 Subject: [PATCH 093/360] Set Magnum environment variables for acceptance tests Add the environment variable Magnume acceptance tests need to the stackenv script and document then in the acceptance test README. --- acceptance/README.md | 8 ++++++++ script/stackenv | 4 ++++ 2 files changed, 12 insertions(+) diff --git a/acceptance/README.md b/acceptance/README.md index b2a5b35101..834cdc12ef 100644 --- a/acceptance/README.md +++ b/acceptance/README.md @@ -73,11 +73,19 @@ to set them manually. |`OS_DB_DATASTORE_VERSION`|The Datastore version to use. Example: `mariadb-10`| #### Shared file systems + |Name|Description| |---|---| |`OS_NETWORK_ID`| The network ID to use when creating shared network| |`OS_SUBNET_ID`| The subnet ID to use when creating shared network| +#### Container infra + +|Name|Description| +|---|---| +|`OS_MAGNUM_IMAGE_ID`| The ID of a valid magnum image| +|`OS_MAGNUM_KEYPAIR`| The ID of a valid keypair| + ### 3. Run the test suite From the root directory, run: diff --git a/script/stackenv b/script/stackenv index fef97cd87c..b110de49e3 100644 --- a/script/stackenv +++ b/script/stackenv @@ -7,12 +7,14 @@ pushd $DEVSTACK_PATH source openrc admin admin openstack flavor create m1.acctest --id 99 --ram 512 --disk 5 --vcpu 1 --ephemeral 10 openstack flavor create m1.resize --id 98 --ram 512 --disk 6 --vcpu 1 --ephemeral 10 +openstack keypair create magnum _NETWORK_ID=$(openstack network show private -c id -f value) _SUBNET_ID=$(openstack subnet show private-subnet -c id -f value) _EXTGW_ID=$(openstack network show public -c id -f value) _IMAGE=$(openstack image list | grep -i cirros | head -n 1) _IMAGE_ID=$(echo $_IMAGE | awk -F\| '{print $2}' | tr -d ' ') _IMAGE_NAME=$(echo $_IMAGE | awk -F\| '{print $3}' | tr -d ' ') +_MAGNUM_IMAGE_ID=$(openstack image list --format value -c Name -c ID | grep coreos | cut -d ' ' -f 1) echo export OS_IMAGE_NAME="$_IMAGE_NAME" >> openrc echo export OS_IMAGE_ID="$_IMAGE_ID" >> openrc echo export OS_NETWORK_ID="$_NETWORK_ID" >> openrc @@ -22,5 +24,7 @@ echo export OS_POOL_NAME="public" >> openrc echo export OS_FLAVOR_ID=99 >> openrc echo export OS_FLAVOR_ID_RESIZE=98 >> openrc echo export OS_DOMAIN_ID=default >> openrc +echo export OS_MAGNUM_IMAGE_ID="$_MAGNUM_IMAGE_ID" >> openrc +echo export OS_MAGNUM_KEYPAIR=magnum >> openrc source openrc admin admin popd From 5688950a6a3019775d63dd90751dba979ee7857d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= <martin.andre@gmail.com> Date: Thu, 30 Dec 2021 19:14:05 +0100 Subject: [PATCH 094/360] Bump disk size to 10GiB Previously, 5GiB were too small for the fedora-coreos image used by magnum tests. --- script/stackenv | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/script/stackenv b/script/stackenv index b110de49e3..3440576f5d 100644 --- a/script/stackenv +++ b/script/stackenv @@ -5,8 +5,8 @@ DEVSTACK_PATH=${DEVSTACK_PATH:-/opt/stack/new/devstack} pushd $DEVSTACK_PATH source openrc admin admin -openstack flavor create m1.acctest --id 99 --ram 512 --disk 5 --vcpu 1 --ephemeral 10 -openstack flavor create m1.resize --id 98 --ram 512 --disk 6 --vcpu 1 --ephemeral 10 +openstack flavor create m1.acctest --id 99 --ram 512 --disk 10 --vcpu 1 --ephemeral 10 +openstack flavor create m1.resize --id 98 --ram 512 --disk 11 --vcpu 1 --ephemeral 10 openstack keypair create magnum _NETWORK_ID=$(openstack network show private -c id -f value) _SUBNET_ID=$(openstack subnet show private-subnet -c id -f value) From 9cb25f89dd7501f8ddaf1402d7917377ac8dbdf9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= <martin.andre@gmail.com> Date: Sat, 7 May 2022 16:19:55 +0200 Subject: [PATCH 095/360] Enable Barbican and keystone admin endpoint --- .github/workflows/functional-containerinfra.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/functional-containerinfra.yaml b/.github/workflows/functional-containerinfra.yaml index f3062a54ea..b684213f15 100644 --- a/.github/workflows/functional-containerinfra.yaml +++ b/.github/workflows/functional-containerinfra.yaml @@ -39,10 +39,12 @@ jobs: with: branch: ${{ matrix.openstack_version }} conf_overrides: | + enable_plugin barbican https://opendev.org/openstack/barbican ${{ matrix.openstack_version }} enable_plugin heat https://opendev.org/openstack/heat ${{ matrix.openstack_version }} enable_plugin magnum https://opendev.org/openstack/magnum ${{ matrix.openstack_version }} GLANCE_LIMIT_IMAGE_SIZE_TOTAL=5000 SWIFT_MAX_FILE_SIZE=5368709122 + KEYSTONE_ADMIN_ENDPOINT=true enabled_services: 'h-eng,h-api,h-api-cfn,h-api-cw' - name: Checkout go uses: actions/setup-go@v2 From 63a180c3edeba19e3a830318310a1c13e7196635 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= <martin.andre@gmail.com> Date: Sun, 8 May 2022 20:14:27 +0200 Subject: [PATCH 096/360] Fix magnum guest image for Ussuri and below The image was named Fedora-AtomicHost back then, instead of fedora-coreos. --- script/stackenv | 3 +++ 1 file changed, 3 insertions(+) diff --git a/script/stackenv b/script/stackenv index 3440576f5d..d51d311e74 100644 --- a/script/stackenv +++ b/script/stackenv @@ -15,6 +15,9 @@ _IMAGE=$(openstack image list | grep -i cirros | head -n 1) _IMAGE_ID=$(echo $_IMAGE | awk -F\| '{print $2}' | tr -d ' ') _IMAGE_NAME=$(echo $_IMAGE | awk -F\| '{print $3}' | tr -d ' ') _MAGNUM_IMAGE_ID=$(openstack image list --format value -c Name -c ID | grep coreos | cut -d ' ' -f 1) +if [ -z "$_MAGNUM_IMAGE_ID" ]; then + _MAGNUM_IMAGE_ID=$(openstack image list --format value -c Name -c ID | grep -i atomic | cut -d ' ' -f 1) +fi echo export OS_IMAGE_NAME="$_IMAGE_NAME" >> openrc echo export OS_IMAGE_ID="$_IMAGE_ID" >> openrc echo export OS_NETWORK_ID="$_NETWORK_ID" >> openrc From 6795a55c97f7fe7430d6cc2a8cc1867195392c75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= <martin.andre@gmail.com> Date: Sun, 8 May 2022 20:27:39 +0200 Subject: [PATCH 097/360] Skip broken tests --- acceptance/openstack/containerinfra/v1/certificates_test.go | 2 ++ acceptance/openstack/containerinfra/v1/clusters_test.go | 1 + acceptance/openstack/containerinfra/v1/nodegroups_test.go | 1 + 3 files changed, 4 insertions(+) diff --git a/acceptance/openstack/containerinfra/v1/certificates_test.go b/acceptance/openstack/containerinfra/v1/certificates_test.go index e0e14a9702..b83f1ac28d 100644 --- a/acceptance/openstack/containerinfra/v1/certificates_test.go +++ b/acceptance/openstack/containerinfra/v1/certificates_test.go @@ -12,6 +12,8 @@ import ( ) func TestCertificatesCRUD(t *testing.T) { + t.Skip("Test must be rewritten to drop hardcoded cluster ID") + client, err := clients.NewContainerInfraV1Client() th.AssertNoErr(t, err) diff --git a/acceptance/openstack/containerinfra/v1/clusters_test.go b/acceptance/openstack/containerinfra/v1/clusters_test.go index 5bc4c6aecd..18d2069bd1 100644 --- a/acceptance/openstack/containerinfra/v1/clusters_test.go +++ b/acceptance/openstack/containerinfra/v1/clusters_test.go @@ -14,6 +14,7 @@ import ( ) func TestClustersCRUD(t *testing.T) { + t.Skip("Failure to deploy cluster in CI") client, err := clients.NewContainerInfraV1Client() th.AssertNoErr(t, err) diff --git a/acceptance/openstack/containerinfra/v1/nodegroups_test.go b/acceptance/openstack/containerinfra/v1/nodegroups_test.go index af44bb98eb..7537d1d436 100644 --- a/acceptance/openstack/containerinfra/v1/nodegroups_test.go +++ b/acceptance/openstack/containerinfra/v1/nodegroups_test.go @@ -16,6 +16,7 @@ import ( ) func TestNodeGroupsCRUD(t *testing.T) { + t.Skip("Failure to deploy cluster in CI") // API not available until Magnum train clients.SkipRelease(t, "stable/mitaka") clients.SkipRelease(t, "stable/newton") From e3cf5631352e0e18e023fa8cb500bf76d5d6a950 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= <martin.andre@gmail.com> Date: Sun, 8 May 2022 20:27:54 +0200 Subject: [PATCH 098/360] Create k8s clusters rather than swarm clusters CI would complain with: ``` convenience.go:35: Failure in clusters_test.go, line 21: unexpected error "Bad request with: [POST http://10.1.1.5/container-infra/v1/clustertemplates], error message: {\"errors\": [{\"request_id\": \"\", \"code\": \"client\", \"status\": 400, \"title\": \"Cluster type (vm, fedora-coreos, swarm) not supported\", \"detail\": \"Cluster type (vm, fedora-coreos, swarm) not supported.\", \"links\": []}]}" ``` --- acceptance/openstack/containerinfra/v1/clusters_test.go | 4 ++-- .../openstack/containerinfra/v1/clustertemplates_test.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/acceptance/openstack/containerinfra/v1/clusters_test.go b/acceptance/openstack/containerinfra/v1/clusters_test.go index 18d2069bd1..67eca18c53 100644 --- a/acceptance/openstack/containerinfra/v1/clusters_test.go +++ b/acceptance/openstack/containerinfra/v1/clusters_test.go @@ -18,11 +18,11 @@ func TestClustersCRUD(t *testing.T) { client, err := clients.NewContainerInfraV1Client() th.AssertNoErr(t, err) - clusterTemplate, err := CreateClusterTemplate(t, client) + clusterTemplate, err := CreateKubernetesClusterTemplate(t, client) th.AssertNoErr(t, err) defer DeleteClusterTemplate(t, client, clusterTemplate.UUID) - clusterID, err := CreateCluster(t, client, clusterTemplate.UUID) + clusterID, err := CreateKubernetesCluster(t, client, clusterTemplate.UUID) th.AssertNoErr(t, err) tools.PrintResource(t, clusterID) defer DeleteCluster(t, client, clusterID) diff --git a/acceptance/openstack/containerinfra/v1/clustertemplates_test.go b/acceptance/openstack/containerinfra/v1/clustertemplates_test.go index b50edffe8a..87994c5c20 100644 --- a/acceptance/openstack/containerinfra/v1/clustertemplates_test.go +++ b/acceptance/openstack/containerinfra/v1/clustertemplates_test.go @@ -16,7 +16,7 @@ func TestClusterTemplatesCRUD(t *testing.T) { client, err := clients.NewContainerInfraV1Client() th.AssertNoErr(t, err) - clusterTemplate, err := CreateClusterTemplate(t, client) + clusterTemplate, err := CreateKubernetesClusterTemplate(t, client) th.AssertNoErr(t, err) t.Log(clusterTemplate.Name) From 692915d3309ccd0d481d919a1199bbe104e4e792 Mon Sep 17 00:00:00 2001 From: shhgs <shhgs@yahoo.com> Date: Mon, 9 May 2022 17:01:24 -0400 Subject: [PATCH 099/360] Neutron v2: BGP Peer Update --- .../v2/extensions/bgp/peers/bgppeers_test.go | 35 +++++++++++++------ .../networking/v2/extensions/bgp/peers/doc.go | 14 ++++++++ .../v2/extensions/bgp/peers/requests.go | 33 ++++++++++++++++- .../v2/extensions/bgp/peers/results.go | 6 ++++ .../extensions/bgp/peers/testing/fixture.go | 23 ++++++++++++ .../bgp/peers/testing/requests_test.go | 27 ++++++++++++++ .../v2/extensions/bgp/peers/urls.go | 5 +++ 7 files changed, 131 insertions(+), 12 deletions(-) diff --git a/acceptance/openstack/networking/v2/extensions/bgp/peers/bgppeers_test.go b/acceptance/openstack/networking/v2/extensions/bgp/peers/bgppeers_test.go index 07f0bc3801..e85896ef69 100644 --- a/acceptance/openstack/networking/v2/extensions/bgp/peers/bgppeers_test.go +++ b/acceptance/openstack/networking/v2/extensions/bgp/peers/bgppeers_test.go @@ -31,31 +31,44 @@ func CreateBGPPeer(t *testing.T, client *gophercloud.ServiceClient) (*peers.BGPP return bgpPeer, err } -func TestBGPPeerCRD(t *testing.T) { +func TestBGPPeerCRUD(t *testing.T) { clients.RequireAdmin(t) client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) - allPages, err := peers.List(client).AllPages() - th.AssertNoErr(t, err) - - allPeers, err := peers.ExtractBGPPeers(allPages) - th.AssertNoErr(t, err) - - t.Logf("Retrieved BGP Peers") - tools.PrintResource(t, allPeers) - + // Create a BGP Peer bgpPeerCreated, err := CreateBGPPeer(t, client) th.AssertNoErr(t, err) - tools.PrintResource(t, bgpPeerCreated) + // Get a BGP Peer bgpPeerGot, err := peers.Get(client, bgpPeerCreated.ID).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, bgpPeerCreated.ID, bgpPeerGot.ID) th.AssertEquals(t, bgpPeerCreated.Name, bgpPeerGot.Name) + // Update a BGP Peer + newBGPPeerName := tools.RandomString("TESTACC-BGPPEER-", 10) + updateBGPOpts := peers.UpdateOpts{ + Name: newBGPPeerName, + Password: tools.MakeNewPassword(""), + } + bgpPeerUpdated, err := peers.Update(client, bgpPeerGot.ID, updateBGPOpts).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, bgpPeerUpdated.Name, newBGPPeerName) + + // List all BGP Peers + allPages, err := peers.List(client).AllPages() + th.AssertNoErr(t, err) + allPeers, err := peers.ExtractBGPPeers(allPages) + th.AssertNoErr(t, err) + + t.Logf("Retrieved BGP Peers") + tools.PrintResource(t, allPeers) + th.AssertIntGreaterOrEqual(t, len(allPeers), 1) + + // Delete a BGP Peer t.Logf("Attempting to delete BGP Peer: %s", bgpPeerGot.Name) err = peers.Delete(client, bgpPeerGot.ID).ExtractErr() th.AssertNoErr(t, err) diff --git a/openstack/networking/v2/extensions/bgp/peers/doc.go b/openstack/networking/v2/extensions/bgp/peers/doc.go index f55764aa5b..e0277b6822 100644 --- a/openstack/networking/v2/extensions/bgp/peers/doc.go +++ b/openstack/networking/v2/extensions/bgp/peers/doc.go @@ -54,4 +54,18 @@ Example: log.Panic(err) } log.Printf("BGP Peer deleted") + + +5. Update BGP Peer, a.k.a. PUT /bgp-peers/{id} + +Example: + + var opt peers.UpdateOpts + opt.Name = "peer-name-updated" + opt.Password = "superStrong" + p, err := peers.Update(c, id, opts).Extract() + if err != nil { + log.Panic(err) + } + log.Printf("%+v", p) */ diff --git a/openstack/networking/v2/extensions/bgp/peers/requests.go b/openstack/networking/v2/extensions/bgp/peers/requests.go index 480c4c584c..bedeb457af 100644 --- a/openstack/networking/v2/extensions/bgp/peers/requests.go +++ b/openstack/networking/v2/extensions/bgp/peers/requests.go @@ -26,7 +26,7 @@ type CreateOptsBuilder interface { ToPeerCreateMap() (map[string]interface{}, error) } -// CreateOpts represents options used to create a network. +// CreateOpts represents options used to create a BGP Peer. type CreateOpts struct { AuthType string `json:"auth_type"` RemoteAS int `json:"remote_as"` @@ -58,3 +58,34 @@ func Delete(c *gophercloud.ServiceClient, bgpPeerID string) (r DeleteResult) { _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } + +// UpdateOptsBuilder allows extensions to add additional parameters to the +// Update request. +type UpdateOptsBuilder interface { + ToPeerUpdateMap() (map[string]interface{}, error) +} + +// UpdateOpts represents options used to update a BGP Peer. +type UpdateOpts struct { + Name string `json:"name,omitempty"` + Password string `json:"password,omitempty"` +} + +// ToPeerUpdateMap builds a request body from UpdateOpts. +func (opts UpdateOpts) ToPeerUpdateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, jroot) +} + +// Update accept a BGP Peer ID and an UpdateOpts and update the BGP Peer +func Update(c *gophercloud.ServiceClient, bgpPeerID string, opts UpdateOpts) (r UpdateResult) { + b, err := opts.ToPeerUpdateMap() + if err != nil { + r.Err = err + return + } + resp, err := c.Put(updateURL(c, bgpPeerID), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} diff --git a/openstack/networking/v2/extensions/bgp/peers/results.go b/openstack/networking/v2/extensions/bgp/peers/results.go index 8fc9f04392..0f87f53f7d 100644 --- a/openstack/networking/v2/extensions/bgp/peers/results.go +++ b/openstack/networking/v2/extensions/bgp/peers/results.go @@ -88,3 +88,9 @@ type CreateResult struct { type DeleteResult struct { gophercloud.ErrResult } + +// UpdateResult represents the result of an update operation. Call its Extract +// method to interpret it as a BGPPeer. +type UpdateResult struct { + commonResult +} diff --git a/openstack/networking/v2/extensions/bgp/peers/testing/fixture.go b/openstack/networking/v2/extensions/bgp/peers/testing/fixture.go index 86d2e0d3bd..67cd77fbf5 100644 --- a/openstack/networking/v2/extensions/bgp/peers/testing/fixture.go +++ b/openstack/networking/v2/extensions/bgp/peers/testing/fixture.go @@ -86,3 +86,26 @@ const CreateResponse = ` } } ` + +const UpdateBGPPeerRequest = ` +{ + "bgp_peer": { + "name": "test-rename-bgp-peer", + "password": "superStrong" + } +} +` + +const UpdateBGPPeerResponse = ` +{ + "bgp_peer": { + "auth_type": "md5", + "remote_as": 20000, + "name": "test-rename-bgp-peer", + "tenant_id": "52a9d4ff-81b6-4b16-a7fa-5325d3bc1c5d", + "peer_ip": "192.168.0.1", + "project_id": "52a9d4ff-81b6-4b16-a7fa-5325d3bc1c5d", + "id": "b7ad63ea-b803-496a-ad59-f9ef513a5cb9" + } +} +` diff --git a/openstack/networking/v2/extensions/bgp/peers/testing/requests_test.go b/openstack/networking/v2/extensions/bgp/peers/testing/requests_test.go index 46ffc94294..83c207ad95 100644 --- a/openstack/networking/v2/extensions/bgp/peers/testing/requests_test.go +++ b/openstack/networking/v2/extensions/bgp/peers/testing/requests_test.go @@ -105,3 +105,30 @@ func TestDelete(t *testing.T) { err := peers.Delete(fake.ServiceClient(), bgpPeerID).ExtractErr() th.AssertNoErr(t, err) } + +func TestUpdate(t *testing.T) { + bgpPeerID := "afacc0e8-6b66-44e4-be53-a1ef16033ceb" + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/bgp-peers/"+bgpPeerID, func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PUT") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestHeader(t, r, "Content-Type", "application/json") + th.TestHeader(t, r, "Accept", "application/json") + th.TestJSONRequest(t, r, UpdateBGPPeerRequest) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, UpdateBGPPeerResponse) + }) + + var opts peers.UpdateOpts + opts.Name = "test-rename-bgp-peer" + opts.Password = "superStrong" + + r, err := peers.Update(fake.ServiceClient(), bgpPeerID, opts).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, r.Name, opts.Name) +} diff --git a/openstack/networking/v2/extensions/bgp/peers/urls.go b/openstack/networking/v2/extensions/bgp/peers/urls.go index 040617b0fb..e63ea79f24 100644 --- a/openstack/networking/v2/extensions/bgp/peers/urls.go +++ b/openstack/networking/v2/extensions/bgp/peers/urls.go @@ -33,3 +33,8 @@ func createURL(c *gophercloud.ServiceClient) string { func deleteURL(c *gophercloud.ServiceClient, id string) string { return resourceURL(c, id) } + +// return /v2.0/bgp-peers/{bgp-peer-id} +func updateURL(c *gophercloud.ServiceClient, id string) string { + return resourceURL(c, id) +} From 661ea44d13e04ad0ced887d4c489e81a40a04638 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= <m.andre@redhat.com> Date: Tue, 10 May 2022 10:31:00 +0200 Subject: [PATCH 100/360] Add stable/yoga to containerinfra functional tests --- .github/workflows/functional-containerinfra.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/functional-containerinfra.yaml b/.github/workflows/functional-containerinfra.yaml index b684213f15..693640e986 100644 --- a/.github/workflows/functional-containerinfra.yaml +++ b/.github/workflows/functional-containerinfra.yaml @@ -14,6 +14,9 @@ jobs: openstack_version: ["master"] ubuntu_version: ["20.04"] include: + - name: "yoga" + openstack_version: "stable/yoga" + ubuntu_version: "20.04" - name: "xena" openstack_version: "stable/xena" ubuntu_version: "20.04" From 7800ad881171c7de05a991ab8b668e8d94d96941 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= <m.andre@redhat.com> Date: Tue, 10 May 2022 10:31:44 +0200 Subject: [PATCH 101/360] Bump dependent actions to be consistent with other workflows --- .github/workflows/functional-containerinfra.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/functional-containerinfra.yaml b/.github/workflows/functional-containerinfra.yaml index 693640e986..1e7f5a607a 100644 --- a/.github/workflows/functional-containerinfra.yaml +++ b/.github/workflows/functional-containerinfra.yaml @@ -36,7 +36,7 @@ jobs: name: Deploy OpenStack ${{ matrix.name }} with Magnum and run containerinfra acceptance tests steps: - name: Checkout Gophercloud - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Deploy devstack uses: EmilienM/devstack-action@v0.6 with: @@ -50,7 +50,7 @@ jobs: KEYSTONE_ADMIN_ENDPOINT=true enabled_services: 'h-eng,h-api,h-api-cfn,h-api-cw' - name: Checkout go - uses: actions/setup-go@v2 + uses: actions/setup-go@v3 with: go-version: '^1.15' - name: Run Gophercloud acceptance tests @@ -63,7 +63,7 @@ jobs: if: failure() - name: Upload logs artifacts on failure if: failure() - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: functional-containerinfra-${{ matrix.name }} path: /tmp/devstack-logs/* From 551f7108c8a44360462c6f1225e58dfba9927b6e Mon Sep 17 00:00:00 2001 From: Arthur Outhenin-Chalandre <arthur.outhenin-chalandre@cern.ch> Date: Thu, 7 Apr 2022 17:55:27 +0200 Subject: [PATCH 102/360] containerinfra: add missing merge_labels in create nodegroup request Signed-off-by: Arthur Outhenin-Chalandre <arthur.outhenin-chalandre@cern.ch> --- openstack/containerinfra/v1/nodegroups/requests.go | 3 ++- .../containerinfra/v1/nodegroups/testing/requests_test.go | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/openstack/containerinfra/v1/nodegroups/requests.go b/openstack/containerinfra/v1/nodegroups/requests.go index 3d694dae2e..fa82e1265d 100644 --- a/openstack/containerinfra/v1/nodegroups/requests.go +++ b/openstack/containerinfra/v1/nodegroups/requests.go @@ -87,7 +87,8 @@ type CreateOpts struct { // Node image ID. Defaults to cluster template image if unset. ImageID string `json:"image_id,omitempty"` // Node machine flavor ID. Defaults to cluster minion flavor if unset. - FlavorID string `json:"flavor_id,omitempty"` + FlavorID string `json:"flavor_id,omitempty"` + MergeLabels *bool `json:"merge_labels,omitempty"` } func (opts CreateOpts) ToNodeGroupCreateMap() (map[string]interface{}, error) { diff --git a/openstack/containerinfra/v1/nodegroups/testing/requests_test.go b/openstack/containerinfra/v1/nodegroups/testing/requests_test.go index 3a758f7b47..d4d1a20aaf 100644 --- a/openstack/containerinfra/v1/nodegroups/testing/requests_test.go +++ b/openstack/containerinfra/v1/nodegroups/testing/requests_test.go @@ -132,7 +132,8 @@ func TestCreateNodeGroupSuccess(t *testing.T) { sc.Endpoint = sc.Endpoint + "v1/" createOpts := nodegroups.CreateOpts{ - Name: "test-ng", + Name: "test-ng", + MergeLabels: gophercloud.Enabled, } ng, err := nodegroups.Create(sc, clusterUUID, createOpts).Extract() From cc57cdedc9bedfb023fea4c69f303496b90daee8 Mon Sep 17 00:00:00 2001 From: Arthur Outhenin-Chalandre <arthur.outhenin-chalandre@cern.ch> Date: Fri, 8 Apr 2022 10:17:31 +0200 Subject: [PATCH 103/360] containerinfra: add missing labels fields in result Signed-off-by: Arthur Outhenin-Chalandre <arthur.outhenin-chalandre@cern.ch> --- openstack/containerinfra/v1/clusters/results.go | 3 +++ openstack/containerinfra/v1/nodegroups/results.go | 3 +++ 2 files changed, 6 insertions(+) diff --git a/openstack/containerinfra/v1/clusters/results.go b/openstack/containerinfra/v1/clusters/results.go index 6f712b5c11..8e8442a940 100644 --- a/openstack/containerinfra/v1/clusters/results.go +++ b/openstack/containerinfra/v1/clusters/results.go @@ -94,6 +94,9 @@ type Cluster struct { FlavorID string `json:"flavor_id"` KeyPair string `json:"keypair"` Labels map[string]string `json:"labels"` + LabelsAdded map[string]string `json:"labels_added"` + LabelsOverridden map[string]string `json:"labels_overridden"` + LabelsSkipped map[string]string `json:"labels_skipped"` Links []gophercloud.Link `json:"links"` MasterFlavorID string `json:"master_flavor_id"` MasterAddresses []string `json:"master_addresses"` diff --git a/openstack/containerinfra/v1/nodegroups/results.go b/openstack/containerinfra/v1/nodegroups/results.go index 994c9c8fd2..5f3e79f1c6 100644 --- a/openstack/containerinfra/v1/nodegroups/results.go +++ b/openstack/containerinfra/v1/nodegroups/results.go @@ -50,6 +50,9 @@ type NodeGroup struct { ProjectID string `json:"project_id"` DockerVolumeSize *int `json:"docker_volume_size"` Labels map[string]string `json:"labels"` + LabelsAdded map[string]string `json:"labels_added"` + LabelsOverridden map[string]string `json:"labels_overridden"` + LabelsSkipped map[string]string `json:"labels_skipped"` Links []gophercloud.Link `json:"links"` FlavorID string `json:"flavor_id"` ImageID string `json:"image_id"` From d9ceb5ba5cb73b723dc2a5ed44b0c6230b95fe01 Mon Sep 17 00:00:00 2001 From: shhgs <shhgs@yahoo.com> Date: Fri, 20 May 2022 09:43:48 -0400 Subject: [PATCH 104/360] Neutron v2: BGP Speaker Update and Add/Remove BGP Peer --- .../v2/extensions/bgp/peers/bgppeers_test.go | 28 +----- .../v2/extensions/bgp/peers/peers.go | 32 +++++++ .../bgp/speakers/bgpspeakers_test.go | 91 +++++++++--------- .../v2/extensions/bgp/speakers/speakers.go | 38 ++++++++ .../v2/extensions/bgp/speakers/doc.go | 44 ++++++++- .../v2/extensions/bgp/speakers/requests.go | 93 ++++++++++++++++++- .../v2/extensions/bgp/speakers/results.go | 31 +++++++ .../bgp/speakers/testing/fixture.go | 33 +++++++ .../bgp/speakers/testing/requests_test.go | 85 +++++++++++++++++ .../v2/extensions/bgp/speakers/urls.go | 15 +++ 10 files changed, 417 insertions(+), 73 deletions(-) create mode 100644 acceptance/openstack/networking/v2/extensions/bgp/peers/peers.go create mode 100644 acceptance/openstack/networking/v2/extensions/bgp/speakers/speakers.go diff --git a/acceptance/openstack/networking/v2/extensions/bgp/peers/bgppeers_test.go b/acceptance/openstack/networking/v2/extensions/bgp/peers/bgppeers_test.go index e85896ef69..37a2e7f94a 100644 --- a/acceptance/openstack/networking/v2/extensions/bgp/peers/bgppeers_test.go +++ b/acceptance/openstack/networking/v2/extensions/bgp/peers/bgppeers_test.go @@ -3,34 +3,12 @@ package peers import ( "testing" - "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/bgp/peers" th "github.com/gophercloud/gophercloud/testhelper" ) -func CreateBGPPeer(t *testing.T, client *gophercloud.ServiceClient) (*peers.BGPPeer, error) { - var opts peers.CreateOpts - opts.AuthType = "md5" - opts.Password = tools.MakeNewPassword("") - opts.RemoteAS = tools.RandomInt(1000, 2000) - opts.Name = tools.RandomString("TESTACC-BGPPEER-", 8) - opts.PeerIP = "192.168.0.1" - - t.Logf("Attempting to create BGP Peer: %s", opts.Name) - bgpPeer, err := peers.Create(client, opts).Extract() - if err != nil { - return bgpPeer, err - } - - t.Logf("Successfully created BGP Peer") - th.AssertEquals(t, bgpPeer.Name, opts.Name) - th.AssertEquals(t, bgpPeer.RemoteAS, opts.RemoteAS) - th.AssertEquals(t, bgpPeer.PeerIP, opts.PeerIP) - return bgpPeer, err -} - func TestBGPPeerCRUD(t *testing.T) { clients.RequireAdmin(t) @@ -40,7 +18,6 @@ func TestBGPPeerCRUD(t *testing.T) { // Create a BGP Peer bgpPeerCreated, err := CreateBGPPeer(t, client) th.AssertNoErr(t, err) - tools.PrintResource(t, bgpPeerCreated) // Get a BGP Peer bgpPeerGot, err := peers.Get(client, bgpPeerCreated.ID).Extract() @@ -57,6 +34,7 @@ func TestBGPPeerCRUD(t *testing.T) { bgpPeerUpdated, err := peers.Update(client, bgpPeerGot.ID, updateBGPOpts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, bgpPeerUpdated.Name, newBGPPeerName) + t.Logf("Update BGP Peer, renamed from %s to %s", bgpPeerGot.Name, bgpPeerUpdated.Name) // List all BGP Peers allPages, err := peers.List(client).AllPages() @@ -69,11 +47,11 @@ func TestBGPPeerCRUD(t *testing.T) { th.AssertIntGreaterOrEqual(t, len(allPeers), 1) // Delete a BGP Peer - t.Logf("Attempting to delete BGP Peer: %s", bgpPeerGot.Name) + t.Logf("Attempting to delete BGP Peer: %s", bgpPeerUpdated.Name) err = peers.Delete(client, bgpPeerGot.ID).ExtractErr() th.AssertNoErr(t, err) bgpPeerGot, err = peers.Get(client, bgpPeerGot.ID).Extract() th.AssertErr(t, err) - t.Logf("BGP Peer %s deleted", bgpPeerCreated.Name) + t.Logf("BGP Peer %s deleted", bgpPeerUpdated.Name) } diff --git a/acceptance/openstack/networking/v2/extensions/bgp/peers/peers.go b/acceptance/openstack/networking/v2/extensions/bgp/peers/peers.go new file mode 100644 index 0000000000..cd0b102dab --- /dev/null +++ b/acceptance/openstack/networking/v2/extensions/bgp/peers/peers.go @@ -0,0 +1,32 @@ +package peers + +import ( + "testing" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/bgp/peers" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func CreateBGPPeer(t *testing.T, client *gophercloud.ServiceClient) (*peers.BGPPeer, error) { + var opts peers.CreateOpts + opts.AuthType = "md5" + opts.Password = tools.MakeNewPassword("") + opts.RemoteAS = tools.RandomInt(1000, 2000) + opts.Name = tools.RandomString("TESTACC-BGPPEER-", 8) + opts.PeerIP = "192.168.0.1" + + t.Logf("Attempting to create BGP Peer: %s", opts.Name) + bgpPeer, err := peers.Create(client, opts).Extract() + if err != nil { + return bgpPeer, err + } + + th.AssertEquals(t, bgpPeer.Name, opts.Name) + th.AssertEquals(t, bgpPeer.RemoteAS, opts.RemoteAS) + th.AssertEquals(t, bgpPeer.PeerIP, opts.PeerIP) + t.Logf("Successfully created BGP Peer") + tools.PrintResource(t, bgpPeer) + return bgpPeer, err +} diff --git a/acceptance/openstack/networking/v2/extensions/bgp/speakers/bgpspeakers_test.go b/acceptance/openstack/networking/v2/extensions/bgp/speakers/bgpspeakers_test.go index 50caca7390..de56cd74d1 100644 --- a/acceptance/openstack/networking/v2/extensions/bgp/speakers/bgpspeakers_test.go +++ b/acceptance/openstack/networking/v2/extensions/bgp/speakers/bgpspeakers_test.go @@ -1,58 +1,28 @@ package speakers import ( - "strconv" "testing" - "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/acceptance/clients" + ap "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2/extensions/bgp/peers" "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/bgp/peers" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/bgp/speakers" th "github.com/gophercloud/gophercloud/testhelper" ) -func CreateBGPSpeaker(t *testing.T, client *gophercloud.ServiceClient) (*speakers.BGPSpeaker, error) { - opts := speakers.CreateOpts{ - IPVersion: 4, - AdvertiseFloatingIPHostRoutes: false, - AdvertiseTenantNetworks: true, - Name: tools.RandomString("TESTACC-BGPSPEAKER-", 8), - LocalAS: "3000", - Networks: []string{}, - } - - t.Logf("Attempting to create BGP Speaker: %s", opts.Name) - bgpSpeaker, err := speakers.Create(client, opts).Extract() - if err != nil { - return bgpSpeaker, err - } - - localas, err := strconv.Atoi(opts.LocalAS) - t.Logf("Successfully created BGP Speaker") - th.AssertEquals(t, bgpSpeaker.Name, opts.Name) - th.AssertEquals(t, bgpSpeaker.LocalAS, localas) - th.AssertEquals(t, bgpSpeaker.IPVersion, opts.IPVersion) - th.AssertEquals(t, bgpSpeaker.AdvertiseTenantNetworks, opts.AdvertiseTenantNetworks) - th.AssertEquals(t, bgpSpeaker.AdvertiseFloatingIPHostRoutes, opts.AdvertiseFloatingIPHostRoutes) - return bgpSpeaker, err -} - -func TestBGPSpeakerCRD(t *testing.T) { +func TestBGPSpeakerCRUD(t *testing.T) { clients.RequireAdmin(t) - client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) // Create a BGP Speaker - bgpSpeakerCreated, err := CreateBGPSpeaker(t, client) + bgpSpeaker, err := CreateBGPSpeaker(t, client) th.AssertNoErr(t, err) - tools.PrintResource(t, bgpSpeakerCreated) - // Get a BGP Speaker - bgpSpeakerGot, err := speakers.Get(client, bgpSpeakerCreated.ID).Extract() + // Create a BGP Peer + bgpPeer, err := ap.CreateBGPPeer(t, client) th.AssertNoErr(t, err) - th.AssertEquals(t, bgpSpeakerCreated.ID, bgpSpeakerGot.ID) - th.AssertEquals(t, bgpSpeakerCreated.Name, bgpSpeakerGot.Name) // List BGP Speakers allPages, err := speakers.List(client).AllPages() @@ -64,13 +34,48 @@ func TestBGPSpeakerCRD(t *testing.T) { tools.PrintResource(t, allSpeakers) th.AssertIntGreaterOrEqual(t, len(allSpeakers), 1) - // Delete a BGP Speaker - t.Logf("Attempting to delete BGP Speaker: %s", bgpSpeakerGot.Name) - err = speakers.Delete(client, bgpSpeakerGot.ID).ExtractErr() + // Update BGP Speaker + opts := speakers.UpdateOpts{ + Name: tools.RandomString("TESTACC-BGPSPEAKER-", 10), + AdvertiseTenantNetworks: false, + AdvertiseFloatingIPHostRoutes: true, + } + speakerUpdated, err := speakers.Update(client, bgpSpeaker.ID, opts).Extract() th.AssertNoErr(t, err) + th.AssertEquals(t, speakerUpdated.Name, opts.Name) + t.Logf("Updated the BGP Speaker, name set from %s to %s", bgpSpeaker.Name, speakerUpdated.Name) - // Confirm the BGP Speaker is deleted - bgpSpeakerGot, err = speakers.Get(client, bgpSpeakerGot.ID).Extract() - th.AssertErr(t, err) - t.Logf("BGP Speaker %s deleted", bgpSpeakerCreated.Name) + // Get a BGP Speaker + bgpSpeakerGot, err := speakers.Get(client, bgpSpeaker.ID).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, bgpSpeaker.ID, bgpSpeakerGot.ID) + th.AssertEquals(t, opts.Name, bgpSpeakerGot.Name) + + // AddBGPPeer + addBGPPeerOpts := speakers.AddBGPPeerOpts{BGPPeerID: bgpPeer.ID} + _, err = speakers.AddBGPPeer(client, bgpSpeaker.ID, addBGPPeerOpts).Extract() + th.AssertNoErr(t, err) + speakerGot, err := speakers.Get(client, bgpSpeaker.ID).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, bgpPeer.ID, speakerGot.Peers[0]) + t.Logf("Successfully added BGP Peer %s to BGP Speaker %s", bgpPeer.Name, speakerUpdated.Name) + + // RemoveBGPPeer + removeBGPPeerOpts := speakers.RemoveBGPPeerOpts{BGPPeerID: bgpPeer.ID} + err = speakers.RemoveBGPPeer(client, bgpSpeaker.ID, removeBGPPeerOpts).ExtractErr() + th.AssertNoErr(t, err) + speakerGot, err = speakers.Get(client, bgpSpeaker.ID).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, len(speakerGot.Networks), 0) + t.Logf("Successfully removed BGP Peer %s to BGP Speaker %s", bgpPeer.Name, speakerUpdated.Name) + + // Delete a BGP Peer + t.Logf("Delete the BGP Peer %s", bgpPeer.Name) + err = peers.Delete(client, bgpPeer.ID).ExtractErr() + th.AssertNoErr(t, err) + + // Delete a BGP Speaker + t.Logf("Delete the BGP Speaker %s", speakerUpdated.Name) + err = speakers.Delete(client, bgpSpeaker.ID).ExtractErr() + th.AssertNoErr(t, err) } diff --git a/acceptance/openstack/networking/v2/extensions/bgp/speakers/speakers.go b/acceptance/openstack/networking/v2/extensions/bgp/speakers/speakers.go new file mode 100644 index 0000000000..00b706c53e --- /dev/null +++ b/acceptance/openstack/networking/v2/extensions/bgp/speakers/speakers.go @@ -0,0 +1,38 @@ +package speakers + +import ( + "strconv" + "testing" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/bgp/speakers" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func CreateBGPSpeaker(t *testing.T, client *gophercloud.ServiceClient) (*speakers.BGPSpeaker, error) { + opts := speakers.CreateOpts{ + IPVersion: 4, + AdvertiseFloatingIPHostRoutes: false, + AdvertiseTenantNetworks: true, + Name: tools.RandomString("TESTACC-BGPSPEAKER-", 8), + LocalAS: "3000", + Networks: []string{}, + } + + t.Logf("Attempting to create BGP Speaker: %s", opts.Name) + bgpSpeaker, err := speakers.Create(client, opts).Extract() + if err != nil { + return bgpSpeaker, err + } + + localas, err := strconv.Atoi(opts.LocalAS) + th.AssertEquals(t, bgpSpeaker.Name, opts.Name) + th.AssertEquals(t, bgpSpeaker.LocalAS, localas) + th.AssertEquals(t, bgpSpeaker.IPVersion, opts.IPVersion) + th.AssertEquals(t, bgpSpeaker.AdvertiseTenantNetworks, opts.AdvertiseTenantNetworks) + th.AssertEquals(t, bgpSpeaker.AdvertiseFloatingIPHostRoutes, opts.AdvertiseFloatingIPHostRoutes) + t.Logf("Successfully created BGP Speaker") + tools.PrintResource(t, bgpSpeaker) + return bgpSpeaker, err +} diff --git a/openstack/networking/v2/extensions/bgp/speakers/doc.go b/openstack/networking/v2/extensions/bgp/speakers/doc.go index 36dfb2cad6..8c46f229ba 100644 --- a/openstack/networking/v2/extensions/bgp/speakers/doc.go +++ b/openstack/networking/v2/extensions/bgp/speakers/doc.go @@ -45,7 +45,7 @@ Example: LocalAS: "2000", Networks: []string{}, } - r, err := speaker.Create(c, opts).Extract() + r, err := speakers.Create(c, opts).Extract() if err != nil { log.Panic(err) } @@ -56,9 +56,49 @@ Example: Example: - err := speaker.Delete(auth, speakerID).ExtractErr() + err := speakers.Delete(auth, speakerID).ExtractErr() if err != nil { log.Panic(err) } log.Printf("Speaker Deleted") + + +6. Update BGP Speaker + +Example: + + opts := speakers.UpdateOpts{ + Name: "testing-bgp-speaker", + AdvertiseTenantNetworks: false, + AdvertiseFloatingIPHostRoutes: true, + } + spk, err := speakers.Update(c, bgpSpeakerID, opts).Extract() + if err != nil { + log.Panic(err) + } + log.Printf("%+v", spk) + + +7. Add BGP Peer, a.k.a. PUT /bgp-speakers/{id}/add_bgp_peer + +Example: + + opts := speakers.AddBGPPeerOpts{BGPPeerID: bgpPeerID} + r, err := speakers.AddBGPPeer(c, bgpSpeakerID, opts).Extract() + if err != nil { + log.Panic(err) + } + log.Printf("%+v", r) + + +8. Remove BGP Peer, a.k.a. PUT /bgp-speakers/{id}/remove_bgp_peer + +Example: + + opts := speakers.RemoveBGPPeerOpts{BGPPeerID: bgpPeerID} + err := speakers.RemoveBGPPeer(c, bgpSpeakerID, opts).ExtractErr() + if err != nil { + log.Panic(err) + } + log.Printf("Successfully removed BGP Peer") */ diff --git a/openstack/networking/v2/extensions/bgp/speakers/requests.go b/openstack/networking/v2/extensions/bgp/speakers/requests.go index 65c0b2f267..efe2c4b13b 100644 --- a/openstack/networking/v2/extensions/bgp/speakers/requests.go +++ b/openstack/networking/v2/extensions/bgp/speakers/requests.go @@ -20,7 +20,7 @@ func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { return } -// CreateOpts represents options used to create a network. +// CreateOpts represents options used to create a BGP Speaker. type CreateOpts struct { Name string `json:"name"` IPVersion int `json:"ip_version"` @@ -30,8 +30,7 @@ type CreateOpts struct { Networks []string `json:"networks,omitempty"` } -// CreateOptsBuilder allows extensions to add additional parameters to the -// Create request. +// CreateOptsBuilder declare a function that build CreateOpts into a Create request body. type CreateOptsBuilder interface { ToSpeakerCreateMap() (map[string]interface{}, error) } @@ -59,3 +58,91 @@ func Delete(c *gophercloud.ServiceClient, speakerID string) (r DeleteResult) { _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } + +// UpdateOpts represents options used to update a BGP Speaker. +type UpdateOpts struct { + Name string `json:"name,omitempty"` + AdvertiseFloatingIPHostRoutes bool `json:"advertise_floating_ip_host_routes"` + AdvertiseTenantNetworks bool `json:"advertise_tenant_networks"` +} + +// ToSpeakerUpdateMap build a request body from UpdateOpts +func (opts UpdateOpts) ToSpeakerUpdateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, jroot) +} + +// UpdateOptsBuilder allow the extensions to add additional parameters to the +// Update request. +type UpdateOptsBuilder interface { + ToSpeakerUpdateMap() (map[string]interface{}, error) +} + +// Update accepts a UpdateOpts and update the BGP Speaker. +func Update(c *gophercloud.ServiceClient, speakerID string, opts UpdateOptsBuilder) (r UpdateResult) { + b, err := opts.ToSpeakerUpdateMap() + if err != nil { + r.Err = err + return + } + resp, err := c.Put(updateURL(c, speakerID), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// AddBGPPeerOpts represents options used to add a BGP Peer to a BGP Speaker +type AddBGPPeerOpts struct { + BGPPeerID string `json:"bgp_peer_id"` +} + +// AddBGPPeerOptsBuilder declare a funtion that encode AddBGPPeerOpts into a request body +type AddBGPPeerOptsBuilder interface { + ToBGPSpeakerAddBGPPeerMap() (map[string]interface{}, error) +} + +// ToBGPSpeakerAddBGPPeerMap build a request body from AddBGPPeerOpts +func (opts AddBGPPeerOpts) ToBGPSpeakerAddBGPPeerMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "") +} + +// AddBGPPeer add the BGP peer to the speaker a.k.a. PUT /v2.0/bgp-speakers/{bgp-speaker-id}/add_bgp_peer +func AddBGPPeer(c *gophercloud.ServiceClient, bgpSpeakerID string, opts AddBGPPeerOptsBuilder) (r AddBGPPeerResult) { + b, err := opts.ToBGPSpeakerAddBGPPeerMap() + if err != nil { + r.Err = err + return + } + resp, err := c.Put(addBGPPeerURL(c, bgpSpeakerID), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// RemoveBGPPeerOpts represents options used to remove a BGP Peer to a BGP Speaker +type RemoveBGPPeerOpts AddBGPPeerOpts + +// RemoveBGPPeerOptsBuilder declare a funtion that encode RemoveBGPPeerOpts into a request body +type RemoveBGPPeerOptsBuilder interface { + ToBGPSpeakerRemoveBGPPeerMap() (map[string]interface{}, error) +} + +// ToBGPSpeakerRemoveBGPPeerMap build a request body from RemoveBGPPeerOpts +func (opts RemoveBGPPeerOpts) ToBGPSpeakerRemoveBGPPeerMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "") +} + +// RemoveBGPPeer remove the BGP peer from the speaker, a.k.a. PUT /v2.0/bgp-speakers/{bgp-speaker-id}/add_bgp_peer +func RemoveBGPPeer(c *gophercloud.ServiceClient, bgpSpeakerID string, opts RemoveBGPPeerOptsBuilder) (r RemoveBGPPeerResult) { + b, err := opts.ToBGPSpeakerRemoveBGPPeerMap() + if err != nil { + r.Err = err + return + } + resp, err := c.Put(removeBGPPeerURL(c, bgpSpeakerID), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} diff --git a/openstack/networking/v2/extensions/bgp/speakers/results.go b/openstack/networking/v2/extensions/bgp/speakers/results.go index c0dd78fb5c..fef68e3349 100644 --- a/openstack/networking/v2/extensions/bgp/speakers/results.go +++ b/openstack/networking/v2/extensions/bgp/speakers/results.go @@ -76,6 +76,9 @@ func ExtractBGPSpeakers(r pagination.Page) ([]BGPSpeaker, error) { return s, err } +// ExtractBGPSpeakersInto accepts a Page struct and an interface{}. The former contains +// a list of BGPSpeaker and the later should be used to store the result that would be +// extracted from the former. func ExtractBGPSpeakersInto(r pagination.Page, v interface{}) error { return r.(BGPSpeakerPage).Result.ExtractIntoSlicePtr(v, "bgp_speakers") } @@ -97,3 +100,31 @@ type CreateResult struct { type DeleteResult struct { gophercloud.ErrResult } + +// UpdateResult represents the result of an update operation. Call its Extract +// method to interpret it as a BGPSpeaker. +type UpdateResult struct { + commonResult +} + +// AddBGPPeerResult represent the response of the PUT /v2.0/bgp-speakers/{bgp-speaker-id}/add-bgp-peer +type AddBGPPeerResult struct { + gophercloud.Result +} + +// Extract is a function that accepts a result and extracts a AddBGPPeerResult resource +func (r AddBGPPeerResult) Extract() (*AddBGPPeerOpts, error) { + var s AddBGPPeerOpts + err := r.ExtractInto(&s) + return &s, err +} + +func (r AddBGPPeerResult) ExtractInto(v interface{}) error { + return r.Result.ExtractIntoStructPtr(v, "") +} + +// RemoveBGPPeerResult represent the response of the PUT /v2.0/bgp-speakers/{bgp-speaker-id}/remove-bgp-peer +// There is no body content for the response of a successful DELETE request. +type RemoveBGPPeerResult struct { + gophercloud.ErrResult +} diff --git a/openstack/networking/v2/extensions/bgp/speakers/testing/fixture.go b/openstack/networking/v2/extensions/bgp/speakers/testing/fixture.go index 85d3a3d403..40539824fc 100644 --- a/openstack/networking/v2/extensions/bgp/speakers/testing/fixture.go +++ b/openstack/networking/v2/extensions/bgp/speakers/testing/fixture.go @@ -89,3 +89,36 @@ const CreateResponse = ` } } ` + +const UpdateBGPSpeakerRequest = ` +{ + "bgp_speaker": { + "advertise_floating_ip_host_routes": true, + "advertise_tenant_networks": false, + "name": "testing-bgp-speaker" + } +} +` + +const UpdateBGPSpeakerResponse = ` +{ + "bgp_speaker": { + "peers": [], + "project_id": "7fa3f96b-17ee-4d1b-8fbf-fe889bb1f1d0", + "name": "testing-bgp-speaker", + "tenant_id": "7fa3f96b-17ee-4d1b-8fbf-fe889bb1f1d0", + "local_as": 2000, + "advertise_tenant_networks": false, + "networks": [], + "ip_version": 4, + "advertise_floating_ip_host_routes": true, + "id": "d25d0036-7f17-49d7-8d02-4bf9dd49d5a9" + } +} +` + +const AddRemoveBGPPeerJSON = ` +{ + "bgp_peer_id": "f5884c7c-71d5-43a3-88b4-1742e97674aa" +} +` diff --git a/openstack/networking/v2/extensions/bgp/speakers/testing/requests_test.go b/openstack/networking/v2/extensions/bgp/speakers/testing/requests_test.go index e35b5dce31..7985b39deb 100644 --- a/openstack/networking/v2/extensions/bgp/speakers/testing/requests_test.go +++ b/openstack/networking/v2/extensions/bgp/speakers/testing/requests_test.go @@ -2,6 +2,7 @@ package testing import ( "fmt" + "io" "net/http" "testing" @@ -109,3 +110,87 @@ func TestDelete(t *testing.T) { err := speakers.Delete(fake.ServiceClient(), bgpSpeakerID).ExtractErr() th.AssertNoErr(t, err) } + +func TestUpdate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + bgpSpeakerID := "ab01ade1-ae62-43c9-8a1f-3c24225b96d8" + th.Mux.HandleFunc("/v2.0/bgp-speakers/"+bgpSpeakerID, func(w http.ResponseWriter, r *http.Request) { + if r.Method == "GET" { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, GetBGPSpeakerResult) + } else if r.Method == "PUT" { + th.TestMethod(t, r, "PUT") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestHeader(t, r, "Content-Type", "application/json") + th.TestHeader(t, r, "Accept", "application/json") + th.TestJSONRequest(t, r, UpdateBGPSpeakerRequest) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, UpdateBGPSpeakerResponse) + } else { + panic("Unexpected Request") + } + }) + + opts := speakers.UpdateOpts{ + Name: "testing-bgp-speaker", + AdvertiseTenantNetworks: false, + AdvertiseFloatingIPHostRoutes: true, + } + + r, err := speakers.Update(fake.ServiceClient(), bgpSpeakerID, opts).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, r.Name, opts.Name) + th.AssertEquals(t, r.AdvertiseTenantNetworks, opts.AdvertiseTenantNetworks) + th.AssertEquals(t, r.AdvertiseFloatingIPHostRoutes, opts.AdvertiseFloatingIPHostRoutes) +} + +func TestAddBGPPeer(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + bgpSpeakerID := "ab01ade1-ae62-43c9-8a1f-3c24225b96d8" + bgpPeerID := "f5884c7c-71d5-43a3-88b4-1742e97674aa" + th.Mux.HandleFunc("/v2.0/bgp-speakers/"+bgpSpeakerID+"/add_bgp_peer", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PUT") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestHeader(t, r, "Content-Type", "application/json") + th.TestHeader(t, r, "Accept", "application/json") + th.TestJSONRequest(t, r, AddRemoveBGPPeerJSON) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, AddRemoveBGPPeerJSON) + }) + + opts := speakers.AddBGPPeerOpts{BGPPeerID: bgpPeerID} + r, err := speakers.AddBGPPeer(fake.ServiceClient(), bgpSpeakerID, opts).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, bgpPeerID, r.BGPPeerID) +} + +func TestRemoveBGPPeer(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + bgpSpeakerID := "ab01ade1-ae62-43c9-8a1f-3c24225b96d8" + bgpPeerID := "f5884c7c-71d5-43a3-88b4-1742e97674aa" + th.Mux.HandleFunc("/v2.0/bgp-speakers/"+bgpSpeakerID+"/remove_bgp_peer", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PUT") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestHeader(t, r, "Content-Type", "application/json") + th.TestHeader(t, r, "Accept", "application/json") + th.TestJSONRequest(t, r, AddRemoveBGPPeerJSON) + w.WriteHeader(http.StatusOK) + }) + + opts := speakers.RemoveBGPPeerOpts{BGPPeerID: bgpPeerID} + err := speakers.RemoveBGPPeer(fake.ServiceClient(), bgpSpeakerID, opts).ExtractErr() + th.AssertEquals(t, err, io.EOF) +} diff --git a/openstack/networking/v2/extensions/bgp/speakers/urls.go b/openstack/networking/v2/extensions/bgp/speakers/urls.go index bf579dc8fe..a691dbf94d 100644 --- a/openstack/networking/v2/extensions/bgp/speakers/urls.go +++ b/openstack/networking/v2/extensions/bgp/speakers/urls.go @@ -33,3 +33,18 @@ func createURL(c *gophercloud.ServiceClient) string { func deleteURL(c *gophercloud.ServiceClient, id string) string { return resourceURL(c, id) } + +// return /v2.0/bgp-speakers/{bgp-peer-id} +func updateURL(c *gophercloud.ServiceClient, id string) string { + return resourceURL(c, id) +} + +// return /v2.0/bgp-speakers/{bgp-speaker-id}/add_bgp_peer +func addBGPPeerURL(c *gophercloud.ServiceClient, speakerID string) string { + return c.ServiceURL(urlBase, speakerID, "add_bgp_peer") +} + +// return /v2.0/bgp-speakers/{bgp-speaker-id}/remove_bgp_peer +func removeBGPPeerURL(c *gophercloud.ServiceClient, speakerID string) string { + return c.ServiceURL(urlBase, speakerID, "remove_bgp_peer") +} From 4a5da24cbc2a90194d1a4bf8921d9944cd8168c0 Mon Sep 17 00:00:00 2001 From: Paul Basov <pbasov@users.noreply.github.com> Date: Sat, 21 May 2022 18:26:59 +0200 Subject: [PATCH 105/360] Add ability to handle 502 and 504 errors (#2245) Additional types to handle 502 Bad Gateway and 504 Gateway Timeout http errors Co-authored-by: Pavel Basov <pbasov@mirantis.com> Co-authored-by: Emilien Macchi <emilien@redhat.com> --- errors.go | 28 ++++++++++++++++++++++++++++ provider_client.go | 10 ++++++++++ testing/errors_test.go | 26 ++++++++++++++++++++++---- 3 files changed, 60 insertions(+), 4 deletions(-) diff --git a/errors.go b/errors.go index 79271d3bec..edba02badf 100644 --- a/errors.go +++ b/errors.go @@ -156,11 +156,21 @@ type ErrDefault500 struct { ErrUnexpectedResponseCode } +// ErrDefault502 is the default error type returned on a 502 HTTP response code. +type ErrDefault502 struct { + ErrUnexpectedResponseCode +} + // ErrDefault503 is the default error type returned on a 503 HTTP response code. type ErrDefault503 struct { ErrUnexpectedResponseCode } +// ErrDefault504 is the default error type returned on a 504 HTTP response code. +type ErrDefault504 struct { + ErrUnexpectedResponseCode +} + func (e ErrDefault400) Error() string { e.DefaultErrString = fmt.Sprintf( "Bad request with: [%s %s], error message: %s", @@ -198,10 +208,16 @@ func (e ErrDefault429) Error() string { func (e ErrDefault500) Error() string { return "Internal Server Error" } +func (e ErrDefault502) Error() string { + return "Bad Gateway" +} func (e ErrDefault503) Error() string { return "The service is currently unable to handle the request due to a temporary" + " overloading or maintenance. This is a temporary condition. Try again later." } +func (e ErrDefault504) Error() string { + return "Gateway Timeout" +} // Err400er is the interface resource error types implement to override the error message // from a 400 error. @@ -257,12 +273,24 @@ type Err500er interface { Error500(ErrUnexpectedResponseCode) error } +// Err502er is the interface resource error types implement to override the error message +// from a 502 error. +type Err502er interface { + Error502(ErrUnexpectedResponseCode) error +} + // Err503er is the interface resource error types implement to override the error message // from a 503 error. type Err503er interface { Error503(ErrUnexpectedResponseCode) error } +// Err504er is the interface resource error types implement to override the error message +// from a 504 error. +type Err504er interface { + Error504(ErrUnexpectedResponseCode) error +} + // ErrTimeOut is the error type returned when an operations times out. type ErrTimeOut struct { BaseError diff --git a/provider_client.go b/provider_client.go index 207ab88af8..aa6bdc5e6b 100644 --- a/provider_client.go +++ b/provider_client.go @@ -556,11 +556,21 @@ func (client *ProviderClient) doRequest(method, url string, options *RequestOpts if error500er, ok := errType.(Err500er); ok { err = error500er.Error500(respErr) } + case http.StatusBadGateway: + err = ErrDefault502{respErr} + if error502er, ok := errType.(Err502er); ok { + err = error502er.Error502(respErr) + } case http.StatusServiceUnavailable: err = ErrDefault503{respErr} if error503er, ok := errType.(Err503er); ok { err = error503er.Error503(respErr) } + case http.StatusGatewayTimeout: + err = ErrDefault504{respErr} + if error504er, ok := errType.(Err504er); ok { + err = error504er.Error504(respErr) + } } if err == nil { diff --git a/testing/errors_test.go b/testing/errors_test.go index ca655839e6..eb379a49d9 100644 --- a/testing/errors_test.go +++ b/testing/errors_test.go @@ -7,19 +7,37 @@ import ( th "github.com/gophercloud/gophercloud/testhelper" ) -func TestGetResponseCode(t *testing.T) { - respErr := gophercloud.ErrUnexpectedResponseCode{ +func returnsUnexpectedResp(code int) gophercloud.ErrUnexpectedResponseCode { + return gophercloud.ErrUnexpectedResponseCode{ URL: "http://example.com", Method: "GET", Expected: []int{200}, - Actual: 404, + Actual: code, Body: nil, ResponseHeader: nil, } +} - var err404 error = gophercloud.ErrDefault404{ErrUnexpectedResponseCode: respErr} +func TestGetResponseCode404(t *testing.T) { + var err404 error = gophercloud.ErrDefault404{ErrUnexpectedResponseCode: returnsUnexpectedResp(404)} err, ok := err404.(gophercloud.StatusCodeError) th.AssertEquals(t, true, ok) th.AssertEquals(t, err.GetStatusCode(), 404) } + +func TestGetResponseCode502(t *testing.T) { + var err502 error = gophercloud.ErrDefault502{ErrUnexpectedResponseCode: returnsUnexpectedResp(502)} + + err, ok := err502.(gophercloud.StatusCodeError) + th.AssertEquals(t, true, ok) + th.AssertEquals(t, err.GetStatusCode(), 502) +} + +func TestGetResponseCode504(t *testing.T) { + var err504 error = gophercloud.ErrDefault504{ErrUnexpectedResponseCode: returnsUnexpectedResp(504)} + + err, ok := err504.(gophercloud.StatusCodeError) + th.AssertEquals(t, true, ok) + th.AssertEquals(t, err.GetStatusCode(), 504) +} From f9aad4a5bed5a470c919e745f6e9f5dbef7fdd8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= <martin.andre@gmail.com> Date: Sun, 22 May 2022 15:01:22 +0200 Subject: [PATCH 106/360] Add missing lb listener fields The loadbalancer listener was missing a few fields. https://docs.openstack.org/api-ref/load-balancer/v2/index.html?expanded=list-listeners-detail#list-listeners Fixes #2402 --- .../loadbalancer/v2/listeners/results.go | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/openstack/loadbalancer/v2/listeners/results.go b/openstack/loadbalancer/v2/listeners/results.go index b446beb695..1a953fc88b 100644 --- a/openstack/loadbalancer/v2/listeners/results.go +++ b/openstack/loadbalancer/v2/listeners/results.go @@ -84,12 +84,38 @@ type Listener struct { // A list of IPv4, IPv6 or mix of both CIDRs AllowedCIDRs []string `json:"allowed_cidrs"` + // List of ciphers in OpenSSL format (colon-separated). See + // https://www.openssl.org/docs/man1.1.1/man1/ciphers.html + // New in version 2.15 + TLSCiphers string `json:"tls_ciphers"` + // A list of TLS protocol versions. Available from microversion 2.17 TLSVersions []string `json:"tls_versions"` // Tags is a list of resource tags. Tags are arbitrarily defined strings // attached to the resource. New in version 2.5 Tags []string `json:"tags"` + + // A list of ALPN protocols. Available protocols: http/1.0, http/1.1, h2 + // New in version 2.20 + ALPNProtocols []string `json:"alpn_protocols"` + + // The TLS client authentication mode. One of the options NONE, OPTIONAL or MANDATORY. + // New in version 2.8 + ClientAuthentication string `json:"client_authentication"` + + // The ref of the key manager service secret containing a PEM format + // client CA certificate bundle for TERMINATED_HTTPS listeners. + // New in version 2.8 + ClientCATLSContainerRef string `json:"client_ca_tls_container_ref"` + + // The URI of the key manager service secret containing a PEM format CA + // revocation list file for TERMINATED_HTTPS listeners. + // New in version 2.8 + ClientCRLContainerRef string `json:"client_crl_container_ref"` + + // The operating status of the resource + OperatingStatus string `json:"operating_status"` } type Stats struct { From 38e16070b243e7706713e9571ab88547d93063ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= <martin.andre@gmail.com> Date: Sun, 22 May 2022 22:38:20 +0200 Subject: [PATCH 107/360] Run the L7 policies tests against HTTP listener Use HTTP listener rather than TCP listener for the L7 policies tests. Jobs would otherwise fail with: Provider 'amphora' does not support a requested option: TCP protocol listeners do not support L7 policies Which is in-line with the documentation [1]: Pools of type SCTP, TCP or UDP cannot be used in L7 policies at this time. [1] https://docs.openstack.org/api-ref/load-balancer/v2/index.html?expanded=create-an-l7-policy-detail#create-an-l7-policy --- .../loadbalancer/v2/loadbalancers_test.go | 198 +++++++++--------- 1 file changed, 94 insertions(+), 104 deletions(-) diff --git a/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go b/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go index 5be7f88c1a..c21663ef35 100644 --- a/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go +++ b/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go @@ -124,16 +124,107 @@ func TestLoadbalancerHTTPCRUD(t *testing.T) { th.AssertNoErr(t, err) defer DeleteL7Policy(t, lbClient, lb.ID, policy.ID) + newDescription := "" + updateL7policyOpts := l7policies.UpdateOpts{ + Description: &newDescription, + } + _, err = l7policies.Update(lbClient, policy.ID, updateL7policyOpts).Extract() + th.AssertNoErr(t, err) + + if err = WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE"); err != nil { + t.Fatalf("Timed out waiting for loadbalancer to become active") + } + + newPolicy, err := l7policies.Get(lbClient, policy.ID).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, newPolicy) + + th.AssertEquals(t, newPolicy.Description, newDescription) + // L7 rule - rule, err := CreateL7Rule(t, lbClient, policy.ID, lb) + rule, err := CreateL7Rule(t, lbClient, newPolicy.ID, lb) th.AssertNoErr(t, err) defer DeleteL7Rule(t, lbClient, lb.ID, policy.ID, rule.ID) + allPages, err := l7policies.ListRules(lbClient, policy.ID, l7policies.ListRulesOpts{}).AllPages() + th.AssertNoErr(t, err) + allRules, err := l7policies.ExtractRules(allPages) + th.AssertNoErr(t, err) + for _, rule := range allRules { + tools.PrintResource(t, rule) + } + + updateL7ruleOpts := l7policies.UpdateRuleOpts{ + RuleType: l7policies.TypePath, + CompareType: l7policies.CompareTypeRegex, + Value: "/images/special*", + } + _, err = l7policies.UpdateRule(lbClient, policy.ID, rule.ID, updateL7ruleOpts).Extract() + th.AssertNoErr(t, err) + + if err = WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE"); err != nil { + t.Fatalf("Timed out waiting for loadbalancer to become active") + } + + newRule, err := l7policies.GetRule(lbClient, newPolicy.ID, rule.ID).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, newRule) + // Pool pool, err := CreatePoolHTTP(t, lbClient, lb) th.AssertNoErr(t, err) defer DeletePool(t, lbClient, lb.ID, pool.ID) + poolName := "" + poolDescription := "" + updatePoolOpts := pools.UpdateOpts{ + Name: &poolName, + Description: &poolDescription, + } + _, err = pools.Update(lbClient, pool.ID, updatePoolOpts).Extract() + th.AssertNoErr(t, err) + + if err = WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE"); err != nil { + t.Fatalf("Timed out waiting for loadbalancer to become active") + } + + newPool, err := pools.Get(lbClient, pool.ID).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, newPool) + th.AssertEquals(t, newPool.Name, poolName) + th.AssertEquals(t, newPool.Description, poolDescription) + + // Update L7policy to redirect to pool + newRedirectURL := "" + updateL7policyOpts = l7policies.UpdateOpts{ + Action: l7policies.ActionRedirectToPool, + RedirectPoolID: &newPool.ID, + RedirectURL: &newRedirectURL, + } + _, err = l7policies.Update(lbClient, policy.ID, updateL7policyOpts).Extract() + th.AssertNoErr(t, err) + + if err := WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE"); err != nil { + t.Fatalf("Timed out waiting for loadbalancer to become active") + } + + newPolicy, err = l7policies.Get(lbClient, policy.ID).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, newPolicy) + + th.AssertEquals(t, newPolicy.Description, newDescription) + th.AssertEquals(t, newPolicy.Action, string(l7policies.ActionRedirectToPool)) + th.AssertEquals(t, newPolicy.RedirectPoolID, newPool.ID) + th.AssertEquals(t, newPolicy.RedirectURL, newRedirectURL) + + // Workaround for proper delete order + defer DeleteL7Policy(t, lbClient, lb.ID, policy.ID) + defer DeleteL7Rule(t, lbClient, lb.ID, policy.ID, rule.ID) + // Member member, err := CreateMember(t, lbClient, lb, pool, subnet.ID, subnet.CIDR) th.AssertNoErr(t, err) @@ -221,112 +312,11 @@ func TestLoadbalancersCRUD(t *testing.T) { tools.PrintResource(t, listenerStats) - // L7 policy - policy, err := CreateL7Policy(t, lbClient, listener, lb) - th.AssertNoErr(t, err) - defer DeleteL7Policy(t, lbClient, lb.ID, policy.ID) - - newDescription := "" - updateL7policyOpts := l7policies.UpdateOpts{ - Description: &newDescription, - } - _, err = l7policies.Update(lbClient, policy.ID, updateL7policyOpts).Extract() - th.AssertNoErr(t, err) - - if err = WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE"); err != nil { - t.Fatalf("Timed out waiting for loadbalancer to become active") - } - - newPolicy, err := l7policies.Get(lbClient, policy.ID).Extract() - th.AssertNoErr(t, err) - - tools.PrintResource(t, newPolicy) - - th.AssertEquals(t, newPolicy.Description, newDescription) - - // L7 rule - rule, err := CreateL7Rule(t, lbClient, newPolicy.ID, lb) - th.AssertNoErr(t, err) - defer DeleteL7Rule(t, lbClient, lb.ID, policy.ID, rule.ID) - - allPages, err := l7policies.ListRules(lbClient, policy.ID, l7policies.ListRulesOpts{}).AllPages() - th.AssertNoErr(t, err) - allRules, err := l7policies.ExtractRules(allPages) - th.AssertNoErr(t, err) - for _, rule := range allRules { - tools.PrintResource(t, rule) - } - - updateL7ruleOpts := l7policies.UpdateRuleOpts{ - RuleType: l7policies.TypePath, - CompareType: l7policies.CompareTypeRegex, - Value: "/images/special*", - } - _, err = l7policies.UpdateRule(lbClient, policy.ID, rule.ID, updateL7ruleOpts).Extract() - th.AssertNoErr(t, err) - - if err = WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE"); err != nil { - t.Fatalf("Timed out waiting for loadbalancer to become active") - } - - newRule, err := l7policies.GetRule(lbClient, newPolicy.ID, rule.ID).Extract() - th.AssertNoErr(t, err) - - tools.PrintResource(t, newRule) - // Pool pool, err := CreatePool(t, lbClient, lb) th.AssertNoErr(t, err) defer DeletePool(t, lbClient, lb.ID, pool.ID) - poolName := "" - poolDescription := "" - updatePoolOpts := pools.UpdateOpts{ - Name: &poolName, - Description: &poolDescription, - } - _, err = pools.Update(lbClient, pool.ID, updatePoolOpts).Extract() - th.AssertNoErr(t, err) - - if err = WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE"); err != nil { - t.Fatalf("Timed out waiting for loadbalancer to become active") - } - - newPool, err := pools.Get(lbClient, pool.ID).Extract() - th.AssertNoErr(t, err) - - tools.PrintResource(t, newPool) - th.AssertEquals(t, newPool.Name, poolName) - th.AssertEquals(t, newPool.Description, poolDescription) - - // Update L7policy to redirect to pool - newRedirectURL := "" - updateL7policyOpts = l7policies.UpdateOpts{ - Action: l7policies.ActionRedirectToPool, - RedirectPoolID: &newPool.ID, - RedirectURL: &newRedirectURL, - } - _, err = l7policies.Update(lbClient, policy.ID, updateL7policyOpts).Extract() - th.AssertNoErr(t, err) - - if err := WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE"); err != nil { - t.Fatalf("Timed out waiting for loadbalancer to become active") - } - - newPolicy, err = l7policies.Get(lbClient, policy.ID).Extract() - th.AssertNoErr(t, err) - - tools.PrintResource(t, newPolicy) - - th.AssertEquals(t, newPolicy.Description, newDescription) - th.AssertEquals(t, newPolicy.Action, string(l7policies.ActionRedirectToPool)) - th.AssertEquals(t, newPolicy.RedirectPoolID, newPool.ID) - th.AssertEquals(t, newPolicy.RedirectURL, newRedirectURL) - - // Workaround for proper delete order - defer DeleteL7Policy(t, lbClient, lb.ID, policy.ID) - defer DeleteL7Rule(t, lbClient, lb.ID, policy.ID, rule.ID) - // Update listener's default pool ID. updateListenerOpts = listeners.UpdateOpts{ DefaultPoolID: &pool.ID, @@ -365,7 +355,7 @@ func TestLoadbalancersCRUD(t *testing.T) { th.AssertEquals(t, newListener.DefaultPoolID, "") // Member - member, err := CreateMember(t, lbClient, lb, newPool, subnet.ID, subnet.CIDR) + member, err := CreateMember(t, lbClient, lb, pool, subnet.ID, subnet.CIDR) th.AssertNoErr(t, err) defer DeleteMember(t, lbClient, lb.ID, pool.ID, member.ID) @@ -414,7 +404,7 @@ func TestLoadbalancersCRUD(t *testing.T) { tools.PrintResource(t, pool) // Monitor - monitor, err := CreateMonitor(t, lbClient, lb, newPool) + monitor, err := CreateMonitor(t, lbClient, lb, pool) th.AssertNoErr(t, err) defer DeleteMonitor(t, lbClient, lb.ID, monitor.ID) From 49f8250ee0bf867cca8a2b4098d3e4bcad7a282d Mon Sep 17 00:00:00 2001 From: Emilien Macchi <emilien@redhat.com> Date: Tue, 24 May 2022 10:53:12 -0400 Subject: [PATCH 108/360] CI: Bump devstack-action to v0.7 That will help to disable Tempest (not needed in our jobs) and therefore bring back the Ussuri job in good shape. --- .github/workflows/functional-baremetal.yaml | 2 +- .github/workflows/functional-basic.yaml | 2 +- .github/workflows/functional-blockstorage.yaml | 2 +- .github/workflows/functional-clustering.yaml | 2 +- .github/workflows/functional-compute.yaml | 2 +- .github/workflows/functional-containerinfra.yaml | 2 +- .github/workflows/functional-dns.yaml | 2 +- .github/workflows/functional-identity.yaml | 2 +- .github/workflows/functional-imageservice.yaml | 2 +- .github/workflows/functional-keymanager.yaml | 2 +- .github/workflows/functional-loadbalancer.yaml | 2 +- .github/workflows/functional-messaging.yaml | 2 +- .github/workflows/functional-networking.yaml | 2 +- .github/workflows/functional-objectstorage.yaml | 2 +- .github/workflows/functional-orchestration.yaml | 2 +- .github/workflows/functional-placement.yaml | 2 +- .github/workflows/functional-sharedfilesystems.yaml | 2 +- 17 files changed, 17 insertions(+), 17 deletions(-) diff --git a/.github/workflows/functional-baremetal.yaml b/.github/workflows/functional-baremetal.yaml index be75e91af0..a4a4038017 100644 --- a/.github/workflows/functional-baremetal.yaml +++ b/.github/workflows/functional-baremetal.yaml @@ -38,7 +38,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.6 + uses: EmilienM/devstack-action@v0.7 with: branch: ${{ matrix.openstack_version }} conf_overrides: | diff --git a/.github/workflows/functional-basic.yaml b/.github/workflows/functional-basic.yaml index eeb8f9f492..78b0f9760b 100644 --- a/.github/workflows/functional-basic.yaml +++ b/.github/workflows/functional-basic.yaml @@ -41,7 +41,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.6 + uses: EmilienM/devstack-action@v0.7 with: branch: ${{ matrix.openstack_version }} enabled_services: 's-account,s-container,s-object,s-proxy' diff --git a/.github/workflows/functional-blockstorage.yaml b/.github/workflows/functional-blockstorage.yaml index f133c78055..6878da6d94 100644 --- a/.github/workflows/functional-blockstorage.yaml +++ b/.github/workflows/functional-blockstorage.yaml @@ -38,7 +38,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.6 + uses: EmilienM/devstack-action@v0.7 with: branch: ${{ matrix.openstack_version }} conf_overrides: | diff --git a/.github/workflows/functional-clustering.yaml b/.github/workflows/functional-clustering.yaml index 7b2835c4b1..0c6247887b 100644 --- a/.github/workflows/functional-clustering.yaml +++ b/.github/workflows/functional-clustering.yaml @@ -38,7 +38,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.6 + uses: EmilienM/devstack-action@v0.7 with: branch: ${{ matrix.openstack_version }} conf_overrides: | diff --git a/.github/workflows/functional-compute.yaml b/.github/workflows/functional-compute.yaml index ef0af52f0e..8e9ad33b49 100644 --- a/.github/workflows/functional-compute.yaml +++ b/.github/workflows/functional-compute.yaml @@ -38,7 +38,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.6 + uses: EmilienM/devstack-action@v0.7 with: branch: ${{ matrix.openstack_version }} conf_overrides: | diff --git a/.github/workflows/functional-containerinfra.yaml b/.github/workflows/functional-containerinfra.yaml index 1e7f5a607a..6cccd1cb3b 100644 --- a/.github/workflows/functional-containerinfra.yaml +++ b/.github/workflows/functional-containerinfra.yaml @@ -38,7 +38,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.6 + uses: EmilienM/devstack-action@v0.7 with: branch: ${{ matrix.openstack_version }} conf_overrides: | diff --git a/.github/workflows/functional-dns.yaml b/.github/workflows/functional-dns.yaml index a06d5a6d67..6e9f6ea389 100644 --- a/.github/workflows/functional-dns.yaml +++ b/.github/workflows/functional-dns.yaml @@ -39,7 +39,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.6 + uses: EmilienM/devstack-action@v0.7 with: branch: ${{ matrix.openstack_version }} conf_overrides: | diff --git a/.github/workflows/functional-identity.yaml b/.github/workflows/functional-identity.yaml index cb631f24c4..fcddd1db5a 100644 --- a/.github/workflows/functional-identity.yaml +++ b/.github/workflows/functional-identity.yaml @@ -38,7 +38,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.6 + uses: EmilienM/devstack-action@v0.7 with: branch: ${{ matrix.openstack_version }} - name: Checkout go diff --git a/.github/workflows/functional-imageservice.yaml b/.github/workflows/functional-imageservice.yaml index 3d8b9c10a9..83a17356e1 100644 --- a/.github/workflows/functional-imageservice.yaml +++ b/.github/workflows/functional-imageservice.yaml @@ -38,7 +38,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.6 + uses: EmilienM/devstack-action@v0.7 with: branch: ${{ matrix.openstack_version }} - name: Checkout go diff --git a/.github/workflows/functional-keymanager.yaml b/.github/workflows/functional-keymanager.yaml index a5c421820c..994aa3fe9b 100644 --- a/.github/workflows/functional-keymanager.yaml +++ b/.github/workflows/functional-keymanager.yaml @@ -38,7 +38,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.6 + uses: EmilienM/devstack-action@v0.7 with: branch: ${{ matrix.openstack_version }} conf_overrides: | diff --git a/.github/workflows/functional-loadbalancer.yaml b/.github/workflows/functional-loadbalancer.yaml index f33310e145..827be255b1 100644 --- a/.github/workflows/functional-loadbalancer.yaml +++ b/.github/workflows/functional-loadbalancer.yaml @@ -38,7 +38,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.6 + uses: EmilienM/devstack-action@v0.7 with: branch: ${{ matrix.openstack_version }} conf_overrides: | diff --git a/.github/workflows/functional-messaging.yaml b/.github/workflows/functional-messaging.yaml index b00e01500f..6a8adf9fac 100644 --- a/.github/workflows/functional-messaging.yaml +++ b/.github/workflows/functional-messaging.yaml @@ -38,7 +38,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.6 + uses: EmilienM/devstack-action@v0.7 with: branch: ${{ matrix.openstack_version }} conf_overrides: | diff --git a/.github/workflows/functional-networking.yaml b/.github/workflows/functional-networking.yaml index b455e3f79f..b0cd9cb474 100644 --- a/.github/workflows/functional-networking.yaml +++ b/.github/workflows/functional-networking.yaml @@ -47,7 +47,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.6 + uses: EmilienM/devstack-action@v0.7 with: branch: ${{ matrix.openstack_version }} conf_overrides: | diff --git a/.github/workflows/functional-objectstorage.yaml b/.github/workflows/functional-objectstorage.yaml index d4768a4a99..85e6107828 100644 --- a/.github/workflows/functional-objectstorage.yaml +++ b/.github/workflows/functional-objectstorage.yaml @@ -38,7 +38,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.6 + uses: EmilienM/devstack-action@v0.7 with: branch: ${{ matrix.openstack_version }} conf_overrides: | diff --git a/.github/workflows/functional-orchestration.yaml b/.github/workflows/functional-orchestration.yaml index 0cccd222da..28d3b291a6 100644 --- a/.github/workflows/functional-orchestration.yaml +++ b/.github/workflows/functional-orchestration.yaml @@ -38,7 +38,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.6 + uses: EmilienM/devstack-action@v0.7 with: branch: ${{ matrix.openstack_version }} conf_overrides: | diff --git a/.github/workflows/functional-placement.yaml b/.github/workflows/functional-placement.yaml index f5795ae449..ea514716ff 100644 --- a/.github/workflows/functional-placement.yaml +++ b/.github/workflows/functional-placement.yaml @@ -38,7 +38,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.6 + uses: EmilienM/devstack-action@v0.7 with: branch: ${{ matrix.openstack_version }} - name: Checkout go diff --git a/.github/workflows/functional-sharedfilesystems.yaml b/.github/workflows/functional-sharedfilesystems.yaml index 130ba19d11..471ab81b34 100644 --- a/.github/workflows/functional-sharedfilesystems.yaml +++ b/.github/workflows/functional-sharedfilesystems.yaml @@ -38,7 +38,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.6 + uses: EmilienM/devstack-action@v0.7 with: branch: ${{ matrix.openstack_version }} conf_overrides: | From b957a55ec3b012b9cb8c998f5588b293ca8378c8 Mon Sep 17 00:00:00 2001 From: shhgs <shhgs@yahoo.com> Date: Tue, 24 May 2022 13:59:27 -0400 Subject: [PATCH 109/360] Neutron v2: AddGatewayNetwork, RemoveGatewayNetwork and GetAdvertisedRoutes --- .../bgp/speakers/bgpspeakers_test.go | 27 ++++++ .../v2/extensions/bgp/speakers/doc.go | 82 ++++++++++++++----- .../v2/extensions/bgp/speakers/requests.go | 65 +++++++++++++++ .../v2/extensions/bgp/speakers/results.go | 51 ++++++++++++ .../bgp/speakers/testing/fixture.go | 25 ++++++ .../bgp/speakers/testing/requests_test.go | 82 +++++++++++++++++++ .../v2/extensions/bgp/speakers/urls.go | 15 ++++ 7 files changed, 327 insertions(+), 20 deletions(-) diff --git a/acceptance/openstack/networking/v2/extensions/bgp/speakers/bgpspeakers_test.go b/acceptance/openstack/networking/v2/extensions/bgp/speakers/bgpspeakers_test.go index de56cd74d1..9a7a9f3cde 100644 --- a/acceptance/openstack/networking/v2/extensions/bgp/speakers/bgpspeakers_test.go +++ b/acceptance/openstack/networking/v2/extensions/bgp/speakers/bgpspeakers_test.go @@ -4,6 +4,7 @@ import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" + networking "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2" ap "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2/extensions/bgp/peers" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/bgp/peers" @@ -34,6 +35,11 @@ func TestBGPSpeakerCRUD(t *testing.T) { tools.PrintResource(t, allSpeakers) th.AssertIntGreaterOrEqual(t, len(allSpeakers), 1) + // Create a network + network, err := networking.CreateNetwork(t, client) + th.AssertNoErr(t, err) + defer networking.DeleteNetwork(t, client, network.ID) + // Update BGP Speaker opts := speakers.UpdateOpts{ Name: tools.RandomString("TESTACC-BGPSPEAKER-", 10), @@ -69,6 +75,27 @@ func TestBGPSpeakerCRUD(t *testing.T) { th.AssertEquals(t, len(speakerGot.Networks), 0) t.Logf("Successfully removed BGP Peer %s to BGP Speaker %s", bgpPeer.Name, speakerUpdated.Name) + // GetAdvertisedRoutes + pages, err := speakers.GetAdvertisedRoutes(client, bgpSpeaker.ID).AllPages() + th.AssertNoErr(t, err) + routes, err := speakers.ExtractAdvertisedRoutes(pages) + th.AssertNoErr(t, err) + th.AssertIntGreaterOrEqual(t, len(routes), 0) + t.Logf("Successfully retrieved advertised routes") + + // AddGatewayNetwork + optsAddGatewayNetwork := speakers.AddGatewayNetworkOpts{NetworkID: network.ID} + r, err := speakers.AddGatewayNetwork(client, bgpSpeaker.ID, optsAddGatewayNetwork).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, r.NetworkID, network.ID) + t.Logf("Successfully added gateway network %s to BGP Speaker", network.ID) + + // RemoveGatewayNetwork + optsRemoveGatewayNetwork := speakers.RemoveGatewayNetworkOpts{NetworkID: network.ID} + err = speakers.RemoveGatewayNetwork(client, bgpSpeaker.ID, optsRemoveGatewayNetwork).ExtractErr() + th.AssertNoErr(t, err) + t.Logf("Successfully removed gateway network %s to BGP Speaker", network.ID) + // Delete a BGP Peer t.Logf("Delete the BGP Peer %s", bgpPeer.Name) err = peers.Delete(client, bgpPeer.ID).ExtractErr() diff --git a/openstack/networking/v2/extensions/bgp/speakers/doc.go b/openstack/networking/v2/extensions/bgp/speakers/doc.go index 8c46f229ba..99f54a08ff 100644 --- a/openstack/networking/v2/extensions/bgp/speakers/doc.go +++ b/openstack/networking/v2/extensions/bgp/speakers/doc.go @@ -37,14 +37,14 @@ Example: Example: - opts := speakers.CreateOpts{ - IPVersion: 6, - AdvertiseFloatingIPHostRoutes: false, - AdvertiseTenantNetworks: true, - Name: "gophercloud-testing-bgp-speaker", - LocalAS: "2000", - Networks: []string{}, - } + opts := speakers.CreateOpts{ + IPVersion: 6, + AdvertiseFloatingIPHostRoutes: false, + AdvertiseTenantNetworks: true, + Name: "gophercloud-testing-bgp-speaker", + LocalAS: "2000", + Networks: []string{}, + } r, err := speakers.Create(c, opts).Extract() if err != nil { log.Panic(err) @@ -67,23 +67,23 @@ Example: Example: - opts := speakers.UpdateOpts{ - Name: "testing-bgp-speaker", - AdvertiseTenantNetworks: false, - AdvertiseFloatingIPHostRoutes: true, - } - spk, err := speakers.Update(c, bgpSpeakerID, opts).Extract() - if err != nil { - log.Panic(err) - } - log.Printf("%+v", spk) + opts := speakers.UpdateOpts{ + Name: "testing-bgp-speaker", + AdvertiseTenantNetworks: false, + AdvertiseFloatingIPHostRoutes: true, + } + spk, err := speakers.Update(c, bgpSpeakerID, opts).Extract() + if err != nil { + log.Panic(err) + } + log.Printf("%+v", spk) 7. Add BGP Peer, a.k.a. PUT /bgp-speakers/{id}/add_bgp_peer Example: - opts := speakers.AddBGPPeerOpts{BGPPeerID: bgpPeerID} + opts := speakers.AddBGPPeerOpts{BGPPeerID: bgpPeerID} r, err := speakers.AddBGPPeer(c, bgpSpeakerID, opts).Extract() if err != nil { log.Panic(err) @@ -95,10 +95,52 @@ Example: Example: - opts := speakers.RemoveBGPPeerOpts{BGPPeerID: bgpPeerID} + opts := speakers.RemoveBGPPeerOpts{BGPPeerID: bgpPeerID} err := speakers.RemoveBGPPeer(c, bgpSpeakerID, opts).ExtractErr() if err != nil { log.Panic(err) } log.Printf("Successfully removed BGP Peer") + + +9. Get advertised routes, a.k.a. GET /bgp-speakers/{id}/get_advertised_routes + +Example: + + pages, err := speakers.GetAdvertisedRoutes(c, speakerID).AllPages() + if err != nil { + log.Panic(err) + } + routes, err := speakers.ExtractAdvertisedRoutes(pages) + if err != nil { + log.Panic(err) + } + for _, r := range routes { + log.Printf("%+v", r) + } + + +10. Add geteway network to BGP Speaker, a.k.a. PUT /bgp-speakers/{id}/add_gateway_network + +Example: + + + opts := speakers.AddGatewayNetworkOpts{NetworkID: networkID} + r, err := speakers.AddGatewayNetwork(c, speakerID, opts).Extract() + if err != nil { + log.Panic(err) + } + log.Printf("%+v", r) + + +11. Remove gateway network to BGP Speaker, a.k.a. PUT /bgp-speakers/{id}/remove_gateway_network + +Example: + + opts := speakers.RemoveGatewayNetworkOpts{NetworkID: networkID} + err := speakers.RemoveGatewayNetwork(c, speakerID, opts).ExtractErr() + if err != nil { + log.Panic(err) + } + log.Printf("Successfully removed gateway network") */ diff --git a/openstack/networking/v2/extensions/bgp/speakers/requests.go b/openstack/networking/v2/extensions/bgp/speakers/requests.go index efe2c4b13b..778cf01119 100644 --- a/openstack/networking/v2/extensions/bgp/speakers/requests.go +++ b/openstack/networking/v2/extensions/bgp/speakers/requests.go @@ -146,3 +146,68 @@ func RemoveBGPPeer(c *gophercloud.ServiceClient, bgpSpeakerID string, opts Remov _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } + +// GetAdvertisedRoutes a.k.a. GET /v2.0/bgp-speakers/{bgp-speaker-id}/get_advertised_routes +func GetAdvertisedRoutes(c *gophercloud.ServiceClient, bgpSpeakerID string) pagination.Pager { + url := getAdvertisedRoutesURL(c, bgpSpeakerID) + return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { + return AdvertisedRoutePage{pagination.SinglePageBase(r)} + }) +} + +// AddGatewayNetworkOptsBuilder declare a function that build AddGatewayNetworkOpts into a request body. +type AddGatewayNetworkOptsBuilder interface { + ToBGPSpeakerAddGatewayNetworkMap() (map[string]interface{}, error) +} + +// AddGatewayNetworkOpts represents the data that would be PUT to the endpoint +type AddGatewayNetworkOpts struct { + // The uuid of the network + NetworkID string `json:"network_id"` +} + +// ToBGPSpeakerAddGatewayNetworkMap implements the function +func (opts AddGatewayNetworkOpts) ToBGPSpeakerAddGatewayNetworkMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "") +} + +// AddGatewayNetwork a.k.a. PUT /v2.0/bgp-speakers/{bgp-speaker-id}/add_gateway_network +func AddGatewayNetwork(c *gophercloud.ServiceClient, bgpSpeakerID string, opts AddGatewayNetworkOptsBuilder) (r AddGatewayNetworkResult) { + b, err := opts.ToBGPSpeakerAddGatewayNetworkMap() + if err != nil { + r.Err = err + return + } + resp, err := c.Put(addGatewayNetworkURL(c, bgpSpeakerID), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// RemoveGatewayNetworkOptsBuilder declare a function that build RemoveGatewayNetworkOpts into a request body. +type RemoveGatewayNetworkOptsBuilder interface { + ToBGPSpeakerRemoveGatewayNetworkMap() (map[string]interface{}, error) +} + +// RemoveGatewayNetworkOpts represent the data that would be PUT to the endpoint +type RemoveGatewayNetworkOpts AddGatewayNetworkOpts + +// ToBGPSpeakerRemoveGatewayNetworkMap implement the function +func (opts RemoveGatewayNetworkOpts) ToBGPSpeakerRemoveGatewayNetworkMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "") +} + +// RemoveGatewayNetwork a.k.a. PUT /v2.0/bgp-speakers/{bgp-speaker-id}/remove_gateway_network +func RemoveGatewayNetwork(c *gophercloud.ServiceClient, bgpSpeakerID string, opts RemoveGatewayNetworkOptsBuilder) (r RemoveGatewayNetworkResult) { + b, err := opts.ToBGPSpeakerRemoveGatewayNetworkMap() + if err != nil { + r.Err = err + return + } + resp, err := c.Put(removeGatewayNetworkURL(c, bgpSpeakerID), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} diff --git a/openstack/networking/v2/extensions/bgp/speakers/results.go b/openstack/networking/v2/extensions/bgp/speakers/results.go index fef68e3349..6d0f444620 100644 --- a/openstack/networking/v2/extensions/bgp/speakers/results.go +++ b/openstack/networking/v2/extensions/bgp/speakers/results.go @@ -128,3 +128,54 @@ func (r AddBGPPeerResult) ExtractInto(v interface{}) error { type RemoveBGPPeerResult struct { gophercloud.ErrResult } + +// AdvertisedRoute represents an advertised route +type AdvertisedRoute struct { + // NextHop IP address + NextHop string `json:"next_hop"` + + // Destination Network + Destination string `json:"destination"` +} + +// AdvertisedRoutePage is the page returned by a pager when you call +type AdvertisedRoutePage struct { + pagination.SinglePageBase +} + +// IsEmpty checks whether a AdvertisedRoutePage struct is empty. +func (r AdvertisedRoutePage) IsEmpty() (bool, error) { + is, err := ExtractAdvertisedRoutes(r) + return len(is) == 0, err +} + +// ExtractAdvertisedRoutes accepts a Page struct, a.k.a. AdvertisedRoutePage struct, +// and extracts the elements into a slice of AdvertisedRoute structs. +func ExtractAdvertisedRoutes(r pagination.Page) ([]AdvertisedRoute, error) { + var s []AdvertisedRoute + err := ExtractAdvertisedRoutesInto(r, &s) + return s, err +} + +// ExtractAdvertisedRoutesInto extract the advertised routes from the first param into the 2nd +func ExtractAdvertisedRoutesInto(r pagination.Page, v interface{}) error { + return r.(AdvertisedRoutePage).Result.ExtractIntoSlicePtr(v, "advertised_routes") +} + +// AddGatewayNetworkResult represents the data that would be PUT to +// /v2.0/bgp-speakers/{bgp-speaker-id}/add_gateway_network +type AddGatewayNetworkResult struct { + gophercloud.Result +} + +func (r AddGatewayNetworkResult) Extract() (*AddGatewayNetworkOpts, error) { + var s AddGatewayNetworkOpts + err := r.ExtractInto(&s) + return &s, err +} + +// RemoveGatewayNetworkResult represents the data that would be PUT to +// /v2.0/bgp-speakers/{bgp-speaker-id}/remove_gateway_network +type RemoveGatewayNetworkResult struct { + gophercloud.ErrResult +} diff --git a/openstack/networking/v2/extensions/bgp/speakers/testing/fixture.go b/openstack/networking/v2/extensions/bgp/speakers/testing/fixture.go index 40539824fc..044291778f 100644 --- a/openstack/networking/v2/extensions/bgp/speakers/testing/fixture.go +++ b/openstack/networking/v2/extensions/bgp/speakers/testing/fixture.go @@ -122,3 +122,28 @@ const AddRemoveBGPPeerJSON = ` "bgp_peer_id": "f5884c7c-71d5-43a3-88b4-1742e97674aa" } ` + +const GetAdvertisedRoutesResult = ` +{ + "advertised_routes": [ + { + "next_hop": "172.17.128.212", + "destination": "172.17.129.192/27" + }, + { + "next_hop": "172.17.128.218", + "destination": "172.17.129.0/27" + }, + { + "next_hop": "172.17.128.231", + "destination": "172.17.129.160/27" + } + ] +} +` + +const AddRemoveGatewayNetworkJSON = ` +{ + "network_id": "ac13bb26-6219-49c3-a880-08847f6830b7" +} +` diff --git a/openstack/networking/v2/extensions/bgp/speakers/testing/requests_test.go b/openstack/networking/v2/extensions/bgp/speakers/testing/requests_test.go index 7985b39deb..bf604a9fab 100644 --- a/openstack/networking/v2/extensions/bgp/speakers/testing/requests_test.go +++ b/openstack/networking/v2/extensions/bgp/speakers/testing/requests_test.go @@ -194,3 +194,85 @@ func TestRemoveBGPPeer(t *testing.T) { err := speakers.RemoveBGPPeer(fake.ServiceClient(), bgpSpeakerID, opts).ExtractErr() th.AssertEquals(t, err, io.EOF) } + +func TestGetAdvertisedRoutes(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + bgpSpeakerID := "ab01ade1-ae62-43c9-8a1f-3c24225b96d8" + th.Mux.HandleFunc("/v2.0/bgp-speakers/"+bgpSpeakerID+"/get_advertised_routes", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, GetAdvertisedRoutesResult) + }) + + count := 0 + speakers.GetAdvertisedRoutes(fake.ServiceClient(), bgpSpeakerID).EachPage( + func(page pagination.Page) (bool, error) { + count++ + actual, err := speakers.ExtractAdvertisedRoutes(page) + + if err != nil { + t.Errorf("Failed to extract Advertised route: %v", err) + return false, nil + } + + expected := []speakers.AdvertisedRoute{ + speakers.AdvertisedRoute{NextHop: "172.17.128.212", Destination: "172.17.129.192/27"}, + speakers.AdvertisedRoute{NextHop: "172.17.128.218", Destination: "172.17.129.0/27"}, + speakers.AdvertisedRoute{NextHop: "172.17.128.231", Destination: "172.17.129.160/27"}, + } + th.CheckDeepEquals(t, count, 1) + th.CheckDeepEquals(t, expected, actual) + return true, nil + }) +} + +func TestAddGatewayNetwork(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + bgpSpeakerID := "ab01ade1-ae62-43c9-8a1f-3c24225b96d8" + networkID := "ac13bb26-6219-49c3-a880-08847f6830b7" + th.Mux.HandleFunc("/v2.0/bgp-speakers/"+bgpSpeakerID+"/add_gateway_network", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PUT") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestHeader(t, r, "Content-Type", "application/json") + th.TestHeader(t, r, "Accept", "application/json") + th.TestJSONRequest(t, r, AddRemoveGatewayNetworkJSON) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, AddRemoveGatewayNetworkJSON) + }) + + opts := speakers.AddGatewayNetworkOpts{NetworkID: networkID} + r, err := speakers.AddGatewayNetwork(fake.ServiceClient(), bgpSpeakerID, opts).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, r.NetworkID, networkID) +} + +func TestRemoveGatewayNetwork(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + bgpSpeakerID := "ab01ade1-ae62-43c9-8a1f-3c24225b96d8" + networkID := "ac13bb26-6219-49c3-a880-08847f6830b7" + th.Mux.HandleFunc("/v2.0/bgp-speakers/"+bgpSpeakerID+"/remove_gateway_network", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PUT") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestHeader(t, r, "Content-Type", "application/json") + th.TestHeader(t, r, "Accept", "application/json") + th.TestJSONRequest(t, r, AddRemoveGatewayNetworkJSON) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, "") + }) + + opts := speakers.RemoveGatewayNetworkOpts{NetworkID: networkID} + err := speakers.RemoveGatewayNetwork(fake.ServiceClient(), bgpSpeakerID, opts).ExtractErr() + th.AssertEquals(t, err, io.EOF) +} diff --git a/openstack/networking/v2/extensions/bgp/speakers/urls.go b/openstack/networking/v2/extensions/bgp/speakers/urls.go index a691dbf94d..42c99e0e5a 100644 --- a/openstack/networking/v2/extensions/bgp/speakers/urls.go +++ b/openstack/networking/v2/extensions/bgp/speakers/urls.go @@ -48,3 +48,18 @@ func addBGPPeerURL(c *gophercloud.ServiceClient, speakerID string) string { func removeBGPPeerURL(c *gophercloud.ServiceClient, speakerID string) string { return c.ServiceURL(urlBase, speakerID, "remove_bgp_peer") } + +// return /v2.0/bgp-speakers/{bgp-speaker-id}/get_advertised_routes +func getAdvertisedRoutesURL(c *gophercloud.ServiceClient, speakerID string) string { + return c.ServiceURL(urlBase, speakerID, "get_advertised_routes") +} + +// return /v2.0/bgp-speakers/{bgp-speaker-id}/add_gateway_network +func addGatewayNetworkURL(c *gophercloud.ServiceClient, speakerID string) string { + return c.ServiceURL(urlBase, speakerID, "add_gateway_network") +} + +// return /v2.0/bgp-speakers/{bgp-speaker-id}/remove_gateway_network +func removeGatewayNetworkURL(c *gophercloud.ServiceClient, speakerID string) string { + return c.ServiceURL(urlBase, speakerID, "remove_gateway_network") +} From 89259579e7680c2a3ff58c33c7307ce754526c3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= <m.andre@redhat.com> Date: Wed, 25 May 2022 08:49:42 +0200 Subject: [PATCH 110/360] CI: fix networking job for train release The `stable/train` branch no longer exist on opendev's neutron-dynamic-routing repository, and it caused devstack to fail installing. Use the github mirror instead that still has the desired branch. --- .github/workflows/functional-networking.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/functional-networking.yaml b/.github/workflows/functional-networking.yaml index b0cd9cb474..11a5d0c0f8 100644 --- a/.github/workflows/functional-networking.yaml +++ b/.github/workflows/functional-networking.yaml @@ -53,7 +53,8 @@ jobs: conf_overrides: | Q_ML2_PLUGIN_EXT_DRIVERS=qos,port_security,dns_domain_keywords enable_plugin neutron-vpnaas https://opendev.org/openstack/neutron-vpnaas ${{ matrix.openstack_version }} - enable_plugin neutron-dynamic-routing https://opendev.org/openstack/neutron-dynamic-routing ${{ matrix.openstack_version }} + # Use github mirroring as stable/train branch no longer exists on opendev git repo + enable_plugin neutron-dynamic-routing https://github.com/openstack/neutron-dynamic-routing ${{ matrix.openstack_version }} ${{ matrix.devstack_conf_overrides }} enabled_services: 'neutron-dns,neutron-qos,neutron-segments,neutron-trunk,neutron-uplink-status-propagation,neutron-network-segment-range,neutron-port-forwarding' - name: Checkout go From b2fb4ba5086fdb7b9e1a04aaf7bd91cb44101a80 Mon Sep 17 00:00:00 2001 From: Damir Sayfutdinov <sayfutdinov@selectel.ru> Date: Tue, 24 May 2022 17:00:47 +0300 Subject: [PATCH 111/360] Identity V3: add role assignments list param include_subtree https://docs.openstack.org/api-ref/identity/v3/?expanded=id627-detail#id627 --- .../openstack/identity/v3/roles_test.go | 68 +++++++++++++++++++ openstack/identity/v3/roles/requests.go | 5 ++ .../identity/v3/roles/testing/fixtures.go | 15 ++++ .../v3/roles/testing/requests_test.go | 24 +++++++ 4 files changed, 112 insertions(+) diff --git a/acceptance/openstack/identity/v3/roles_test.go b/acceptance/openstack/identity/v3/roles_test.go index 051f838d0f..f61bddf0a4 100644 --- a/acceptance/openstack/identity/v3/roles_test.go +++ b/acceptance/openstack/identity/v3/roles_test.go @@ -170,6 +170,74 @@ func TestRolesFilterList(t *testing.T) { th.AssertEquals(t, found, false) } +func TestRoleListAssignmentIncludeNamesAndSubtree(t *testing.T) { + clients.RequireAdmin(t) + + client, err := clients.NewIdentityV3Client() + th.AssertNoErr(t, err) + + project, err := CreateProject(t, client, nil) + th.AssertNoErr(t, err) + defer DeleteProject(t, client, project.ID) + + domainID := "default" + roleCreateOpts := roles.CreateOpts{ + DomainID: domainID, + } + role, err := CreateRole(t, client, &roleCreateOpts) + th.AssertNoErr(t, err) + defer DeleteRole(t, client, role.ID) + + user, err := CreateUser(t, client, nil) + th.AssertNoErr(t, err) + defer DeleteUser(t, client, user.ID) + + t.Logf("Attempting to assign a role %s to a user %s on a project %s", + role.Name, user.Name, project.Name) + + assignOpts := roles.AssignOpts{ + UserID: user.ID, + ProjectID: project.ID, + } + err = roles.Assign(client, role.ID, assignOpts).ExtractErr() + th.AssertNoErr(t, err) + + t.Logf("Successfully assigned a role %s to a user %s on a project %s", + role.Name, user.Name, project.Name) + + defer UnassignRole(t, client, role.ID, &roles.UnassignOpts{ + UserID: user.ID, + ProjectID: project.ID, + }) + + iTrue := true + listAssignmentsOpts := roles.ListAssignmentsOpts{ + UserID: user.ID, + ScopeProjectID: domainID, // set domainID in ScopeProjectID field to list assignments on all projects in domain + IncludeSubtree: &iTrue, + IncludeNames: &iTrue, + } + allPages, err := roles.ListAssignments(client, listAssignmentsOpts).AllPages() + th.AssertNoErr(t, err) + + allRoles, err := roles.ExtractRoleAssignments(allPages) + th.AssertNoErr(t, err) + + t.Logf("Role assignments(with names) of user %s on projects in domain %s:", user.Name, domainID) + var found bool + for _, _role := range allRoles { + tools.PrintResource(t, _role) + if _role.Role.ID == role.ID && + _role.User.Name == user.Name && + _role.Scope.Project.Name == project.Name && + _role.Scope.Project.Domain.ID == domainID { + found = true + } + } + + th.AssertEquals(t, found, true) +} + func TestRoleListAssignmentForUserOnProject(t *testing.T) { clients.RequireAdmin(t) diff --git a/openstack/identity/v3/roles/requests.go b/openstack/identity/v3/roles/requests.go index 851dae0a6e..2ee925f671 100644 --- a/openstack/identity/v3/roles/requests.go +++ b/openstack/identity/v3/roles/requests.go @@ -208,6 +208,11 @@ type ListAssignmentsOpts struct { // IncludeNames indicates whether to include names of any returned entities. // Requires microversion 3.6 or later. IncludeNames *bool `q:"include_names"` + + // IncludeSubtree indicates whether to include relevant assignments in the project hierarchy below the project + // specified in the ScopeProjectID. Specify DomainID in ScopeProjectID to get a list for all projects in the domain. + // Requires microversion 3.6 or later. + IncludeSubtree *bool `q:"include_subtree"` } // ToRolesListAssignmentsQuery formats a ListAssignmentsOpts into a query string. diff --git a/openstack/identity/v3/roles/testing/fixtures.go b/openstack/identity/v3/roles/testing/fixtures.go index 33d8f02e2a..65809d1c50 100644 --- a/openstack/identity/v3/roles/testing/fixtures.go +++ b/openstack/identity/v3/roles/testing/fixtures.go @@ -428,6 +428,21 @@ func HandleListRoleAssignmentsWithNamesSuccessfully(t *testing.T) { }) } +// HandleListRoleAssignmentsWithSubtreeSuccessfully creates an HTTP handler at `/role_assignments` on the +// test handler mux that responds with a list of two role assignments. +func HandleListRoleAssignmentsWithSubtreeSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/role_assignments", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "Accept", "application/json") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.AssertEquals(t, "include_subtree=true", r.URL.RawQuery) + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, ListAssignmentOutput) + }) +} + // RoleOnResource is the role in the ListAssignmentsOnResource request. var RoleOnResource = roles.Role{ ID: "9fe1d3", diff --git a/openstack/identity/v3/roles/testing/requests_test.go b/openstack/identity/v3/roles/testing/requests_test.go index d574d8ba93..229cfcf7d1 100644 --- a/openstack/identity/v3/roles/testing/requests_test.go +++ b/openstack/identity/v3/roles/testing/requests_test.go @@ -171,6 +171,30 @@ func TestListAssignmentsWithNamesSinglePage(t *testing.T) { th.CheckEquals(t, count, 1) } +func TestListAssignmentsWithSubtreeSinglePage(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListRoleAssignmentsWithSubtreeSuccessfully(t) + + var includeSubtree = true + listOpts := roles.ListAssignmentsOpts{ + IncludeSubtree: &includeSubtree, + } + + count := 0 + err := roles.ListAssignments(client.ServiceClient(), listOpts).EachPage(func(page pagination.Page) (bool, error) { + count++ + actual, err := roles.ExtractRoleAssignments(page) + th.AssertNoErr(t, err) + + th.CheckDeepEquals(t, ExpectedRoleAssignmentsSlice, actual) + + return true, nil + }) + th.AssertNoErr(t, err) + th.CheckEquals(t, count, 1) +} + func TestListAssignmentsOnResource_ProjectsUsers(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() From 64070e349b3739ae780ff06ae9159350262defed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= <m.andre@redhat.com> Date: Fri, 27 May 2022 16:56:23 +0200 Subject: [PATCH 112/360] Update changelog in preparation for v0.25.0 --- CHANGELOG.md | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 25b52a4fb4..e2d568d914 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,48 @@ +## 0.25.0 (Unreleased) + +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 `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 From fd6c4b313e0a445fe1871c65261b121380c32f2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= <m.andre@redhat.com> Date: Fri, 27 May 2022 17:11:50 +0200 Subject: [PATCH 113/360] Fix train networking job It was only a matter of time before the branch sync happened on github and stable/train disappeared. Let's use the train-eol tag instead. --- .github/workflows/functional-networking.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/functional-networking.yaml b/.github/workflows/functional-networking.yaml index 11a5d0c0f8..e830e0f79a 100644 --- a/.github/workflows/functional-networking.yaml +++ b/.github/workflows/functional-networking.yaml @@ -41,6 +41,7 @@ jobs: ubuntu_version: "18.04" devstack_conf_overrides: | enable_plugin neutron-fwaas https://git.openstack.org/openstack/neutron-fwaas stable/train + enable_plugin neutron-dynamic-routing https://opendev.org/openstack/neutron-dynamic-routing train-eol runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Deploy OpenStack ${{ matrix.name }} with Neutron and run networking acceptance tests steps: @@ -53,8 +54,7 @@ jobs: conf_overrides: | Q_ML2_PLUGIN_EXT_DRIVERS=qos,port_security,dns_domain_keywords enable_plugin neutron-vpnaas https://opendev.org/openstack/neutron-vpnaas ${{ matrix.openstack_version }} - # Use github mirroring as stable/train branch no longer exists on opendev git repo - enable_plugin neutron-dynamic-routing https://github.com/openstack/neutron-dynamic-routing ${{ matrix.openstack_version }} + enable_plugin neutron-dynamic-routing https://opendev.org/openstack/neutron-dynamic-routing ${{ matrix.openstack_version }} ${{ matrix.devstack_conf_overrides }} enabled_services: 'neutron-dns,neutron-qos,neutron-segments,neutron-trunk,neutron-uplink-status-propagation,neutron-network-segment-range,neutron-port-forwarding' - name: Checkout go From 7b73397c6ef3f7e70353f761aff5e89d6342da46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= <m.andre@redhat.com> Date: Fri, 27 May 2022 17:13:11 +0200 Subject: [PATCH 114/360] Prefer opendev.org to git.openstack.org --- .github/workflows/functional-networking.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/functional-networking.yaml b/.github/workflows/functional-networking.yaml index e830e0f79a..9a9563fcb8 100644 --- a/.github/workflows/functional-networking.yaml +++ b/.github/workflows/functional-networking.yaml @@ -35,12 +35,12 @@ jobs: openstack_version: "stable/ussuri" ubuntu_version: "18.04" devstack_conf_overrides: | - enable_plugin neutron-fwaas https://git.openstack.org/openstack/neutron-fwaas stable/ussuri + enable_plugin neutron-fwaas https://opendev.org/openstack/neutron-fwaas stable/ussuri - name: "train" openstack_version: "stable/train" ubuntu_version: "18.04" devstack_conf_overrides: | - enable_plugin neutron-fwaas https://git.openstack.org/openstack/neutron-fwaas stable/train + enable_plugin neutron-fwaas https://opendev.org/openstack/neutron-fwaas stable/train enable_plugin neutron-dynamic-routing https://opendev.org/openstack/neutron-dynamic-routing train-eol runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Deploy OpenStack ${{ matrix.name }} with Neutron and run networking acceptance tests From 40c07a8e37125282896596d8fb2fad01f788cf11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= <m.andre@redhat.com> Date: Fri, 27 May 2022 17:59:42 +0200 Subject: [PATCH 115/360] Move neutron-dynamic-routing install per version In order to avoid devstack error about enabling the neutron-dynamic-routing twice, we need to move the instruction per release. --- .github/workflows/functional-networking.yaml | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/.github/workflows/functional-networking.yaml b/.github/workflows/functional-networking.yaml index 9a9563fcb8..7c372a6035 100644 --- a/.github/workflows/functional-networking.yaml +++ b/.github/workflows/functional-networking.yaml @@ -13,29 +13,34 @@ jobs: name: ["master"] openstack_version: ["master"] ubuntu_version: ["20.04"] - devstack_conf_overrides: [""] + devstack_conf_overrides: ["enable_plugin neutron-dynamic-routing https://opendev.org/openstack/neutron-dynamic-routing master"] include: - name: "yoga" openstack_version: "stable/yoga" ubuntu_version: "20.04" - devstack_conf_overrides: "" + devstack_conf_overrides: | + enable_plugin neutron-dynamic-routing https://opendev.org/openstack/neutron-dynamic-routing stable/yoga - name: "xena" openstack_version: "stable/xena" ubuntu_version: "20.04" - devstack_conf_overrides: "" + devstack_conf_overrides: | + enable_plugin neutron-dynamic-routing https://opendev.org/openstack/neutron-dynamic-routing stable/xena - name: "wallaby" openstack_version: "stable/wallaby" ubuntu_version: "20.04" - devstack_conf_overrides: "" + devstack_conf_overrides: | + enable_plugin neutron-dynamic-routing https://opendev.org/openstack/neutron-dynamic-routing stable/wallaby - name: "victoria" openstack_version: "stable/victoria" ubuntu_version: "20.04" - devstack_conf_overrides: "" + devstack_conf_overrides: | + enable_plugin neutron-dynamic-routing https://opendev.org/openstack/neutron-dynamic-routing stable/victoria - name: "ussuri" openstack_version: "stable/ussuri" ubuntu_version: "18.04" devstack_conf_overrides: | enable_plugin neutron-fwaas https://opendev.org/openstack/neutron-fwaas stable/ussuri + enable_plugin neutron-dynamic-routing https://opendev.org/openstack/neutron-dynamic-routing stable/ussuri - name: "train" openstack_version: "stable/train" ubuntu_version: "18.04" @@ -54,7 +59,6 @@ jobs: conf_overrides: | Q_ML2_PLUGIN_EXT_DRIVERS=qos,port_security,dns_domain_keywords enable_plugin neutron-vpnaas https://opendev.org/openstack/neutron-vpnaas ${{ matrix.openstack_version }} - enable_plugin neutron-dynamic-routing https://opendev.org/openstack/neutron-dynamic-routing ${{ matrix.openstack_version }} ${{ matrix.devstack_conf_overrides }} enabled_services: 'neutron-dns,neutron-qos,neutron-segments,neutron-trunk,neutron-uplink-status-propagation,neutron-network-segment-range,neutron-port-forwarding' - name: Checkout go From 414c4585d6c85a6b03b931b40d3e0a64756405e5 Mon Sep 17 00:00:00 2001 From: Emilien Macchi <emilien@redhat.com> Date: Mon, 30 May 2022 11:05:12 -0400 Subject: [PATCH 116/360] Prepare a new release: 0.25.0 --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e2d568d914..e5824a0216 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -## 0.25.0 (Unreleased) +## 0.25.0 (May 30, 2022) BREAKING CHANGES @@ -7,6 +7,7 @@ BREAKING CHANGES 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) From 9f6ee3334c23379dc57ec4b6a2e1464d979db0d8 Mon Sep 17 00:00:00 2001 From: Pierre Prinetti <pierreprinetti@redhat.com> Date: Mon, 27 Jun 2022 11:23:21 +0200 Subject: [PATCH 117/360] Credit the Gophercloud authors for post-2013 contributions. --- LICENSE | 1 + 1 file changed, 1 insertion(+) diff --git a/LICENSE b/LICENSE index fbbbc9e4cb..c3f4f2f7c9 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,5 @@ 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 From 5b4f7ed531b45ed561603ac6a921fcdbd0328304 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=91=E5=8E=9F=E7=94=9F=E9=A9=BF=E7=AB=99?= <33771248+CloudCourierStation@users.noreply.github.com> Date: Thu, 14 Jul 2022 22:31:11 +0800 Subject: [PATCH 118/360] Add support for deleting nova service (#2427) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add support for deleting nova service * Add support for deleting nova service * fix Delete Result value * fix service delete response ok code * modify test * modify test * modify test * modify test Co-authored-by: 吴典秋 <muti_kube@163.com> --- openstack/compute/v2/extensions/services/doc.go | 7 +++++++ openstack/compute/v2/extensions/services/requests.go | 9 +++++++++ openstack/compute/v2/extensions/services/results.go | 6 ++++++ .../v2/extensions/services/testing/fixtures.go | 10 ++++++++++ .../v2/extensions/services/testing/requests_test.go | 11 +++++++++++ 5 files changed, 43 insertions(+) diff --git a/openstack/compute/v2/extensions/services/doc.go b/openstack/compute/v2/extensions/services/doc.go index c960cb1640..3b2f6ab05e 100644 --- a/openstack/compute/v2/extensions/services/doc.go +++ b/openstack/compute/v2/extensions/services/doc.go @@ -32,6 +32,13 @@ Example of updating a service if err != nil { panic(err) } + +Example of delete a service + + updated, err := services.Delete(client, serviceID).Extract() + if err != nil { + panic(err) + } */ package services diff --git a/openstack/compute/v2/extensions/services/requests.go b/openstack/compute/v2/extensions/services/requests.go index 3533df9c06..841fac40ac 100644 --- a/openstack/compute/v2/extensions/services/requests.go +++ b/openstack/compute/v2/extensions/services/requests.go @@ -80,3 +80,12 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOpts) (r Up _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } + +// Delete will delete the existing service with the provided ID. +func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := client.Delete(updateURL(client, id), &gophercloud.RequestOpts{ + OkCodes: []int{204}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} diff --git a/openstack/compute/v2/extensions/services/results.go b/openstack/compute/v2/extensions/services/results.go index 866c54162f..6c56918fcd 100644 --- a/openstack/compute/v2/extensions/services/results.go +++ b/openstack/compute/v2/extensions/services/results.go @@ -109,3 +109,9 @@ func ExtractServices(r pagination.Page) ([]Service, error) { err := (r.(ServicePage)).ExtractInto(&s) return s.Service, err } + +// DeleteResult is the response from a Delete operation. Call its ExtractErr +// method to determine if the call succeeded or failed. +type DeleteResult struct { + gophercloud.ErrResult +} diff --git a/openstack/compute/v2/extensions/services/testing/fixtures.go b/openstack/compute/v2/extensions/services/testing/fixtures.go index 876d68a538..9a2813ea07 100644 --- a/openstack/compute/v2/extensions/services/testing/fixtures.go +++ b/openstack/compute/v2/extensions/services/testing/fixtures.go @@ -288,3 +288,13 @@ func HandleUpdateSuccessfully(t *testing.T) { fmt.Fprintf(w, ServiceUpdate) }) } + +// HandleDeleteSuccessfully configures the test server to respond to a Delete +// request to a Compute server with Pike+ release. +func HandleDeleteSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/os-services/fake-service-id", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "DELETE") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + w.WriteHeader(http.StatusNoContent) + }) +} diff --git a/openstack/compute/v2/extensions/services/testing/requests_test.go b/openstack/compute/v2/extensions/services/testing/requests_test.go index fc75471a7c..cd47353a05 100644 --- a/openstack/compute/v2/extensions/services/testing/requests_test.go +++ b/openstack/compute/v2/extensions/services/testing/requests_test.go @@ -90,3 +90,14 @@ func TestUpdateService(t *testing.T) { testhelper.CheckDeepEquals(t, FakeServiceUpdateBody, *actual) } + +func TestDeleteService(t *testing.T) { + testhelper.SetupHTTP() + defer testhelper.TeardownHTTP() + HandleDeleteSuccessfully(t) + + client := client.ServiceClient() + res := services.Delete(client, "fake-service-id") + + testhelper.AssertNoErr(t, res.Err) +} From b721815123bf6a3e1489d8ffea296e89cddb27b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= <m.andre@redhat.com> Date: Mon, 18 Jul 2022 11:13:26 +0900 Subject: [PATCH 119/360] Do not install tempest in sharedfilesystems jobs Because `MANILA_INSTALL_TEMPEST_PLUGIN_SYSTEMWIDE` defaults to true in manila's devstack plugin [1], we're getting tempest plugin. This is problematic for us because: - we don't want to spend extra time installing unneeded dependencies - this makes it more likely to break, as shown with #2440. This commit sets `MANILA_INSTALL_TEMPEST_PLUGIN_SYSTEMWIDE` to false in the Share Filesystems job. Fixes #2440. [1] https://opendev.org/openstack/manila/src/commit/205d716/devstack/settings#L208 --- .github/workflows/functional-sharedfilesystems.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/functional-sharedfilesystems.yaml b/.github/workflows/functional-sharedfilesystems.yaml index 471ab81b34..7884bda683 100644 --- a/.github/workflows/functional-sharedfilesystems.yaml +++ b/.github/workflows/functional-sharedfilesystems.yaml @@ -56,6 +56,7 @@ jobs: SHARE_BACKING_FILE_SIZE=32000M MANILA_DEFAULT_SHARE_TYPE_EXTRA_SPECS='snapshot_support=True create_share_from_snapshot_support=True revert_to_snapshot_support=True mount_snapshot_support=True' MANILA_CONFIGURE_DEFAULT_TYPES=True + MANILA_INSTALL_TEMPEST_PLUGIN_SYSTEMWIDE=false - name: Checkout go uses: actions/setup-go@v3 with: From 0721d75e876f20e0f3f9be83b3133fa3476ee992 Mon Sep 17 00:00:00 2001 From: Andreas Karis <ak.karis@gmail.com> Date: Thu, 14 Jul 2022 19:57:59 +0200 Subject: [PATCH 120/360] Add support for standard-attr-revisions to core network components The Resource revision numbers extension allows tracking if an object was updated since it was last retrieved. Add support for revision numbers for the 3 core networking objects (networks, subnets, ports). Signed-off-by: Andreas Karis <ak.karis@gmail.com> --- .../openstack/networking/v2/networks_test.go | 63 +++++++++++++++++ .../openstack/networking/v2/ports_test.go | 66 ++++++++++++++++++ .../openstack/networking/v2/subnets_test.go | 67 +++++++++++++++++++ openstack/networking/v2/networks/requests.go | 20 +++++- openstack/networking/v2/networks/results.go | 3 + .../v2/networks/testing/requests_test.go | 44 ++++++++++++ openstack/networking/v2/ports/requests.go | 18 ++++- openstack/networking/v2/ports/results.go | 3 + .../v2/ports/testing/requests_test.go | 51 ++++++++++++++ openstack/networking/v2/subnets/requests.go | 21 +++++- openstack/networking/v2/subnets/results.go | 3 + .../v2/subnets/testing/requests_test.go | 50 ++++++++++++++ 12 files changed, 406 insertions(+), 3 deletions(-) diff --git a/acceptance/openstack/networking/v2/networks_test.go b/acceptance/openstack/networking/v2/networks_test.go index 26ebee0855..cf8a88e60d 100644 --- a/acceptance/openstack/networking/v2/networks_test.go +++ b/acceptance/openstack/networking/v2/networks_test.go @@ -4,6 +4,7 @@ package v2 import ( + "strings" "testing" "github.com/gophercloud/gophercloud/acceptance/clients" @@ -152,3 +153,65 @@ func TestNetworksPortSecurityCRUD(t *testing.T) { tools.PrintResource(t, networkWithExtensions) } + +func TestNetworksRevision(t *testing.T) { + client, err := clients.NewNetworkV2Client() + th.AssertNoErr(t, err) + + // Create a network + network, err := CreateNetwork(t, client) + th.AssertNoErr(t, err) + defer DeleteNetwork(t, client, network.ID) + + tools.PrintResource(t, network) + + // Store the current revision number. + oldRevisionNumber := network.RevisionNumber + + // Update the network without revision number. + // This should work. + newName := tools.RandomString("TESTACC-", 8) + newDescription := "" + updateOpts := &networks.UpdateOpts{ + Name: &newName, + Description: &newDescription, + } + network, err = networks.Update(client, network.ID, updateOpts).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, network) + + // This should fail due to an old revision number. + newDescription = "new description" + updateOpts = &networks.UpdateOpts{ + Name: &newName, + Description: &newDescription, + RevisionNumber: &oldRevisionNumber, + } + _, err = networks.Update(client, network.ID, updateOpts).Extract() + th.AssertErr(t, err) + if !strings.Contains(err.Error(), "RevisionNumberConstraintFailed") { + t.Fatalf("expected to see an error of type RevisionNumberConstraintFailed, but got the following error instead: %v", err) + } + + // Reread the network to show that it did not change. + network, err = networks.Get(client, network.ID).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, network) + + // This should work because now we do provide a valid revision number. + newDescription = "new description" + updateOpts = &networks.UpdateOpts{ + Name: &newName, + Description: &newDescription, + RevisionNumber: &network.RevisionNumber, + } + network, err = networks.Update(client, network.ID, updateOpts).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, network) + + th.AssertEquals(t, network.Name, newName) + th.AssertEquals(t, network.Description, newDescription) +} diff --git a/acceptance/openstack/networking/v2/ports_test.go b/acceptance/openstack/networking/v2/ports_test.go index 966b42db7c..815a5c53b7 100644 --- a/acceptance/openstack/networking/v2/ports_test.go +++ b/acceptance/openstack/networking/v2/ports_test.go @@ -4,6 +4,7 @@ package v2 import ( + "strings" "testing" "github.com/gophercloud/gophercloud/acceptance/clients" @@ -375,3 +376,68 @@ func TestPortsWithExtraDHCPOptsCRUD(t *testing.T) { tools.PrintResource(t, newPort) } + +func TestPortsRevision(t *testing.T) { + client, err := clients.NewNetworkV2Client() + th.AssertNoErr(t, err) + + // Create Network + network, err := CreateNetwork(t, client) + th.AssertNoErr(t, err) + defer DeleteNetwork(t, client, network.ID) + + // Create Subnet + subnet, err := CreateSubnet(t, client, network.ID) + th.AssertNoErr(t, err) + defer DeleteSubnet(t, client, subnet.ID) + + // Create port + port, err := CreatePort(t, client, network.ID, subnet.ID) + th.AssertNoErr(t, err) + defer DeletePort(t, client, port.ID) + + tools.PrintResource(t, port) + + // Add an address pair to the port + // Use the RevisionNumber to test the revision / If-Match logic. + updateOpts := ports.UpdateOpts{ + AllowedAddressPairs: &[]ports.AddressPair{ + {IPAddress: "192.168.255.10", MACAddress: "aa:bb:cc:dd:ee:ff"}, + }, + RevisionNumber: &port.RevisionNumber, + } + newPort, err := ports.Update(client, port.ID, updateOpts).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, newPort) + + // Remove the address pair - this should fail due to old revision number. + updateOpts = ports.UpdateOpts{ + AllowedAddressPairs: &[]ports.AddressPair{}, + RevisionNumber: &port.RevisionNumber, + } + newPort, err = ports.Update(client, port.ID, updateOpts).Extract() + th.AssertErr(t, err) + if !strings.Contains(err.Error(), "RevisionNumberConstraintFailed") { + t.Fatalf("expected to see an error of type RevisionNumberConstraintFailed, but got the following error instead: %v", err) + } + + // The previous ports.Update returns an empty object, so get the port again. + newPort, err = ports.Get(client, port.ID).Extract() + th.AssertNoErr(t, err) + tools.PrintResource(t, newPort) + + // When not specifying a RevisionNumber, then the If-Match mechanism + // should be bypassed. + updateOpts = ports.UpdateOpts{ + AllowedAddressPairs: &[]ports.AddressPair{}, + } + newPort, err = ports.Update(client, port.ID, updateOpts).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, newPort) + + if len(newPort.AllowedAddressPairs) > 0 { + t.Fatalf("Unable to remove the address pair") + } +} diff --git a/acceptance/openstack/networking/v2/subnets_test.go b/acceptance/openstack/networking/v2/subnets_test.go index 104047cad5..95f5a6833a 100644 --- a/acceptance/openstack/networking/v2/subnets_test.go +++ b/acceptance/openstack/networking/v2/subnets_test.go @@ -265,3 +265,70 @@ func TestSubnetDNSNameservers(t *testing.T) { tools.PrintResource(t, newSubnet) th.AssertEquals(t, len(newSubnet.DNSNameservers), 0) } + +func TestSubnetsRevision(t *testing.T) { + client, err := clients.NewNetworkV2Client() + th.AssertNoErr(t, err) + + // Create Network + network, err := CreateNetwork(t, client) + th.AssertNoErr(t, err) + defer DeleteNetwork(t, client, network.ID) + + // Create Subnet + subnet, err := CreateSubnet(t, client, network.ID) + th.AssertNoErr(t, err) + defer DeleteSubnet(t, client, subnet.ID) + + tools.PrintResource(t, subnet) + + // Store the current revision number. + oldRevisionNumber := subnet.RevisionNumber + + // Update Subnet without revision number. + // This should work. + newSubnetName := tools.RandomString("TESTACC-", 8) + newSubnetDescription := "" + updateOpts := &subnets.UpdateOpts{ + Name: &newSubnetName, + Description: &newSubnetDescription, + } + subnet, err = subnets.Update(client, subnet.ID, updateOpts).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, subnet) + + // This should fail due to an old revision number. + newSubnetDescription = "new description" + updateOpts = &subnets.UpdateOpts{ + Name: &newSubnetName, + Description: &newSubnetDescription, + RevisionNumber: &oldRevisionNumber, + } + _, err = subnets.Update(client, subnet.ID, updateOpts).Extract() + th.AssertErr(t, err) + if !strings.Contains(err.Error(), "RevisionNumberConstraintFailed") { + t.Fatalf("expected to see an error of type RevisionNumberConstraintFailed, but got the following error instead: %v", err) + } + + // Reread the subnet to show that it did not change. + subnet, err = subnets.Get(client, subnet.ID).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, subnet) + + // This should work because now we do provide a valid revision number. + newSubnetDescription = "new description" + updateOpts = &subnets.UpdateOpts{ + Name: &newSubnetName, + Description: &newSubnetDescription, + RevisionNumber: &subnet.RevisionNumber, + } + subnet, err = subnets.Update(client, subnet.ID, updateOpts).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, subnet) + + th.AssertEquals(t, subnet.Name, newSubnetName) + th.AssertEquals(t, subnet.Description, newSubnetDescription) +} diff --git a/openstack/networking/v2/networks/requests.go b/openstack/networking/v2/networks/requests.go index 7a28ba0d19..00c2eae77d 100644 --- a/openstack/networking/v2/networks/requests.go +++ b/openstack/networking/v2/networks/requests.go @@ -1,6 +1,8 @@ package networks import ( + "fmt" + "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) @@ -117,6 +119,11 @@ type UpdateOpts struct { Name *string `json:"name,omitempty"` Description *string `json:"description,omitempty"` Shared *bool `json:"shared,omitempty"` + + // RevisionNumber implements extension:standard-attr-revisions. If != "" it + // will set revision_number=%s. If the revision number does not match, the + // update will fail. + RevisionNumber *int `json:"-" h:"If-Match"` } // ToNetworkUpdateMap builds a request body from UpdateOpts. @@ -132,8 +139,19 @@ func Update(c *gophercloud.ServiceClient, networkID string, opts UpdateOptsBuild r.Err = err return } + h, err := gophercloud.BuildHeaders(opts) + if err != nil { + r.Err = err + return + } + for k := range h { + if k == "If-Match" { + h[k] = fmt.Sprintf("revision_number=%s", h[k]) + } + } resp, err := c.Put(updateURL(c, networkID), b, &r.Body, &gophercloud.RequestOpts{ - OkCodes: []int{200, 201}, + MoreHeaders: h, + OkCodes: []int{200, 201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return diff --git a/openstack/networking/v2/networks/results.go b/openstack/networking/v2/networks/results.go index 80ca45c06e..4e175ce557 100644 --- a/openstack/networking/v2/networks/results.go +++ b/openstack/networking/v2/networks/results.go @@ -90,6 +90,9 @@ type Network struct { // Tags optionally set via extensions/attributestags Tags []string `json:"tags"` + + // RevisionNumber optionally set via extensions/standard-attr-revisions + RevisionNumber int `json:"revision_number"` } func (r *Network) UnmarshalJSON(b []byte) error { diff --git a/openstack/networking/v2/networks/testing/requests_test.go b/openstack/networking/v2/networks/testing/requests_test.go index 5721c77c7d..151ffdd82f 100644 --- a/openstack/networking/v2/networks/testing/requests_test.go +++ b/openstack/networking/v2/networks/testing/requests_test.go @@ -221,6 +221,50 @@ func TestUpdate(t *testing.T) { th.AssertEquals(t, n.UpdatedAt.Format(time.RFC3339), "2019-06-30T05:18:49Z") } +func TestUpdateRevision(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/networks/4e8e5957-649f-477b-9e5b-f1f75b21c03c", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PUT") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestHeader(t, r, "Content-Type", "application/json") + th.TestHeader(t, r, "Accept", "application/json") + th.TestHeaderUnset(t, r, "If-Match") + th.TestJSONRequest(t, r, UpdateRequest) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, UpdateResponse) + }) + + th.Mux.HandleFunc("/v2.0/networks/4e8e5957-649f-477b-9e5b-f1f75b21c03d", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PUT") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestHeader(t, r, "Content-Type", "application/json") + th.TestHeader(t, r, "Accept", "application/json") + th.TestHeader(t, r, "If-Match", "revision_number=42") + th.TestJSONRequest(t, r, UpdateRequest) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, UpdateResponse) + }) + + iTrue, iFalse := true, false + name := "new_network_name" + options := networks.UpdateOpts{Name: &name, AdminStateUp: &iFalse, Shared: &iTrue} + _, err := networks.Update(fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c", options).Extract() + th.AssertNoErr(t, err) + + revisionNumber := 42 + options.RevisionNumber = &revisionNumber + _, err = networks.Update(fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03d", options).Extract() + th.AssertNoErr(t, err) +} + func TestDelete(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() diff --git a/openstack/networking/v2/ports/requests.go b/openstack/networking/v2/ports/requests.go index 4dc1600726..57daf19a6f 100644 --- a/openstack/networking/v2/ports/requests.go +++ b/openstack/networking/v2/ports/requests.go @@ -158,6 +158,11 @@ type UpdateOpts struct { DeviceOwner *string `json:"device_owner,omitempty"` SecurityGroups *[]string `json:"security_groups,omitempty"` AllowedAddressPairs *[]AddressPair `json:"allowed_address_pairs,omitempty"` + + // RevisionNumber implements extension:standard-attr-revisions. If != "" it + // will set revision_number=%s. If the revision number does not match, the + // update will fail. + RevisionNumber *int `json:"-" h:"If-Match"` } // ToPortUpdateMap builds a request body from UpdateOpts. @@ -173,8 +178,19 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r r.Err = err return } + h, err := gophercloud.BuildHeaders(opts) + if err != nil { + r.Err = err + return + } + for k := range h { + if k == "If-Match" { + h[k] = fmt.Sprintf("revision_number=%s", h[k]) + } + } resp, err := c.Put(updateURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ - OkCodes: []int{200, 201}, + MoreHeaders: h, + OkCodes: []int{200, 201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return diff --git a/openstack/networking/v2/ports/results.go b/openstack/networking/v2/ports/results.go index 3941b62300..abb3da780d 100644 --- a/openstack/networking/v2/ports/results.go +++ b/openstack/networking/v2/ports/results.go @@ -107,6 +107,9 @@ type Port struct { // Tags optionally set via extensions/attributestags Tags []string `json:"tags"` + + // RevisionNumber optionally set via extensions/standard-attr-revisions + RevisionNumber int `json:"revision_number"` } // PortPage is the page returned by a pager when traversing over a collection diff --git a/openstack/networking/v2/ports/testing/requests_test.go b/openstack/networking/v2/ports/testing/requests_test.go index 529540dd25..7b4dd4a68c 100644 --- a/openstack/networking/v2/ports/testing/requests_test.go +++ b/openstack/networking/v2/ports/testing/requests_test.go @@ -482,6 +482,57 @@ func TestUpdatePortSecurity(t *testing.T) { th.AssertEquals(t, portWithExt.PortSecurityEnabled, false) } +func TestUpdateRevision(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/ports/65c0ee9f-d634-4522-8954-51021b570b0d", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PUT") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestHeader(t, r, "Content-Type", "application/json") + th.TestHeader(t, r, "Accept", "application/json") + th.TestHeaderUnset(t, r, "If-Match") + th.TestJSONRequest(t, r, UpdateRequest) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, UpdateResponse) + }) + th.Mux.HandleFunc("/v2.0/ports/65c0ee9f-d634-4522-8954-51021b570b0e", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PUT") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestHeader(t, r, "Content-Type", "application/json") + th.TestHeader(t, r, "Accept", "application/json") + th.TestHeader(t, r, "If-Match", "revision_number=42") + th.TestJSONRequest(t, r, UpdateRequest) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, UpdateResponse) + }) + + name := "new_port_name" + options := ports.UpdateOpts{ + Name: &name, + FixedIPs: []ports.IP{ + {SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.3"}, + }, + SecurityGroups: &[]string{"f0ac4394-7e4a-4409-9701-ba8be283dbc3"}, + AllowedAddressPairs: &[]ports.AddressPair{ + {IPAddress: "10.0.0.4", MACAddress: "fa:16:3e:c9:cb:f0"}, + }, + } + _, err := ports.Update(fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d", options).Extract() + th.AssertNoErr(t, err) + + revisionNumber := 42 + options.RevisionNumber = &revisionNumber + _, err = ports.Update(fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0e", options).Extract() + th.AssertNoErr(t, err) +} + func TestRemoveSecurityGroups(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() diff --git a/openstack/networking/v2/subnets/requests.go b/openstack/networking/v2/subnets/requests.go index 94a5b6b1aa..7d97fb259d 100644 --- a/openstack/networking/v2/subnets/requests.go +++ b/openstack/networking/v2/subnets/requests.go @@ -1,6 +1,8 @@ package subnets import ( + "fmt" + "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) @@ -197,6 +199,11 @@ type UpdateOpts struct { // EnableDHCP will either enable to disable the DHCP service. EnableDHCP *bool `json:"enable_dhcp,omitempty"` + + // RevisionNumber implements extension:standard-attr-revisions. If != "" it + // will set revision_number=%s. If the revision number does not match, the + // update will fail. + RevisionNumber *int `json:"-" h:"If-Match"` } // ToSubnetUpdateMap builds a request body from UpdateOpts. @@ -221,8 +228,20 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r r.Err = err return } + h, err := gophercloud.BuildHeaders(opts) + if err != nil { + r.Err = err + return + } + for k := range h { + if k == "If-Match" { + h[k] = fmt.Sprintf("revision_number=%s", h[k]) + } + } + resp, err := c.Put(updateURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ - OkCodes: []int{200, 201}, + MoreHeaders: h, + OkCodes: []int{200, 201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return diff --git a/openstack/networking/v2/subnets/results.go b/openstack/networking/v2/subnets/results.go index cf0397019a..e04d486fd4 100644 --- a/openstack/networking/v2/subnets/results.go +++ b/openstack/networking/v2/subnets/results.go @@ -112,6 +112,9 @@ type Subnet struct { // Tags optionally set via extensions/attributestags Tags []string `json:"tags"` + + // RevisionNumber optionally set via extensions/standard-attr-revisions + RevisionNumber int `json:"revision_number"` } // SubnetPage is the page returned by a pager when traversing over a collection diff --git a/openstack/networking/v2/subnets/testing/requests_test.go b/openstack/networking/v2/subnets/testing/requests_test.go index abd75319ee..34a008599f 100644 --- a/openstack/networking/v2/subnets/testing/requests_test.go +++ b/openstack/networking/v2/subnets/testing/requests_test.go @@ -601,6 +601,56 @@ func TestUpdateAllocationPool(t *testing.T) { }) } +func TestUpdateRevision(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/subnets/08eae331-0402-425a-923c-34f7cfe39c1b", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PUT") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestHeader(t, r, "Content-Type", "application/json") + th.TestHeader(t, r, "Accept", "application/json") + th.TestHeaderUnset(t, r, "If-Match") + th.TestJSONRequest(t, r, SubnetUpdateRequest) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusCreated) + + fmt.Fprintf(w, SubnetUpdateResponse) + }) + + th.Mux.HandleFunc("/v2.0/subnets/08eae331-0402-425a-923c-34f7cfe39c1c", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PUT") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestHeader(t, r, "Content-Type", "application/json") + th.TestHeader(t, r, "Accept", "application/json") + th.TestHeader(t, r, "If-Match", "revision_number=42") + th.TestJSONRequest(t, r, SubnetUpdateRequest) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusCreated) + + fmt.Fprintf(w, SubnetUpdateResponse) + }) + + dnsNameservers := []string{"foo"} + name := "my_new_subnet" + opts := subnets.UpdateOpts{ + Name: &name, + DNSNameservers: &dnsNameservers, + HostRoutes: &[]subnets.HostRoute{ + {NextHop: "bar"}, + }, + } + _, err := subnets.Update(fake.ServiceClient(), "08eae331-0402-425a-923c-34f7cfe39c1b", opts).Extract() + th.AssertNoErr(t, err) + + revisionNumber := 42 + opts.RevisionNumber = &revisionNumber + _, err = subnets.Update(fake.ServiceClient(), "08eae331-0402-425a-923c-34f7cfe39c1c", opts).Extract() + th.AssertNoErr(t, err) +} + func TestDelete(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() From 762122f28bc6f7e848c6691ed59a72737af04a52 Mon Sep 17 00:00:00 2001 From: Matt Vinall <boyvinall@gmail.com> Date: Tue, 2 Aug 2022 07:41:44 +0100 Subject: [PATCH 121/360] Add UpdatedAt to ports.Port All neutron core resources have created_at and updated_at, see [class Timestamp](https://github.com/openstack/neutron/blob/b7fad3dd35ad23303a5f52ec64478860a137775e/neutron/extensions/timestamp.py#L33). Signed-off-by: Matt Vinall <boyvinall@gmail.com> --- openstack/networking/v2/ports/results.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/openstack/networking/v2/ports/results.go b/openstack/networking/v2/ports/results.go index abb3da780d..7f8d5ef251 100644 --- a/openstack/networking/v2/ports/results.go +++ b/openstack/networking/v2/ports/results.go @@ -1,6 +1,8 @@ package ports import ( + "time" + "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) @@ -110,6 +112,9 @@ type Port struct { // RevisionNumber optionally set via extensions/standard-attr-revisions RevisionNumber int `json:"revision_number"` + + // Timestamp when the port was last updated + UpdatedAt time.Time `json:"updated_at"` } // PortPage is the page returned by a pager when traversing over a collection From eac628b32b202d60d44b51acf9529d9bdaf0acf0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= <m.andre@redhat.com> Date: Mon, 22 Aug 2022 09:41:33 +0200 Subject: [PATCH 122/360] Fix train and ussuri networking jobs neutron-vpnaas recently removed stable/ussuri and stable/train branches. We now need to use the ussuri-eol and train-eol tags instead when pulling from the project. Fixes #2447 --- .github/workflows/functional-networking.yaml | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/.github/workflows/functional-networking.yaml b/.github/workflows/functional-networking.yaml index 7c372a6035..315ed5a2ae 100644 --- a/.github/workflows/functional-networking.yaml +++ b/.github/workflows/functional-networking.yaml @@ -10,43 +10,51 @@ jobs: strategy: fail-fast: false matrix: - name: ["master"] - openstack_version: ["master"] - ubuntu_version: ["20.04"] - devstack_conf_overrides: ["enable_plugin neutron-dynamic-routing https://opendev.org/openstack/neutron-dynamic-routing master"] include: + - name: "master" + openstack_version: "master" + ubuntu_version: "20.04" + devstack_conf_overrides: | + enable_plugin neutron-dynamic-routing https://opendev.org/openstack/neutron-dynamic-routing master + enable_plugin neutron-vpnaas https://opendev.org/openstack/neutron-vpnaas master - name: "yoga" openstack_version: "stable/yoga" ubuntu_version: "20.04" devstack_conf_overrides: | enable_plugin neutron-dynamic-routing https://opendev.org/openstack/neutron-dynamic-routing stable/yoga + enable_plugin neutron-vpnaas https://opendev.org/openstack/neutron-vpnaas stable/yoga - name: "xena" openstack_version: "stable/xena" ubuntu_version: "20.04" devstack_conf_overrides: | enable_plugin neutron-dynamic-routing https://opendev.org/openstack/neutron-dynamic-routing stable/xena + enable_plugin neutron-vpnaas https://opendev.org/openstack/neutron-vpnaas stable/xena - name: "wallaby" openstack_version: "stable/wallaby" ubuntu_version: "20.04" devstack_conf_overrides: | enable_plugin neutron-dynamic-routing https://opendev.org/openstack/neutron-dynamic-routing stable/wallaby + enable_plugin neutron-vpnaas https://opendev.org/openstack/neutron-vpnaas stable/wallaby - name: "victoria" openstack_version: "stable/victoria" ubuntu_version: "20.04" devstack_conf_overrides: | enable_plugin neutron-dynamic-routing https://opendev.org/openstack/neutron-dynamic-routing stable/victoria + enable_plugin neutron-vpnaas https://opendev.org/openstack/neutron-vpnaas stable/victoria - name: "ussuri" openstack_version: "stable/ussuri" ubuntu_version: "18.04" devstack_conf_overrides: | enable_plugin neutron-fwaas https://opendev.org/openstack/neutron-fwaas stable/ussuri enable_plugin neutron-dynamic-routing https://opendev.org/openstack/neutron-dynamic-routing stable/ussuri + enable_plugin neutron-vpnaas https://opendev.org/openstack/neutron-vpnaas ussuri-eol - name: "train" openstack_version: "stable/train" ubuntu_version: "18.04" devstack_conf_overrides: | enable_plugin neutron-fwaas https://opendev.org/openstack/neutron-fwaas stable/train enable_plugin neutron-dynamic-routing https://opendev.org/openstack/neutron-dynamic-routing train-eol + enable_plugin neutron-vpnaas https://opendev.org/openstack/neutron-vpnaas train-eol runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Deploy OpenStack ${{ matrix.name }} with Neutron and run networking acceptance tests steps: @@ -58,7 +66,6 @@ jobs: branch: ${{ matrix.openstack_version }} conf_overrides: | Q_ML2_PLUGIN_EXT_DRIVERS=qos,port_security,dns_domain_keywords - enable_plugin neutron-vpnaas https://opendev.org/openstack/neutron-vpnaas ${{ matrix.openstack_version }} ${{ matrix.devstack_conf_overrides }} enabled_services: 'neutron-dns,neutron-qos,neutron-segments,neutron-trunk,neutron-uplink-status-propagation,neutron-network-segment-range,neutron-port-forwarding' - name: Checkout go From 37fb9a1ed6105cc73bdf12f7a95cff93719c0126 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= <m.andre@redhat.com> Date: Mon, 22 Aug 2022 13:30:46 +0200 Subject: [PATCH 123/360] Apply gofmt to comments With go 1.19, automated go formatting with `go fmt` applies to comments as well. This caused the unit test job to fail on new PRs. Let's apply the go 1.19 formatting to the whole code base. Fixes #2449 --- acceptance/openstack/compute/v2/compute.go | 2 +- .../networking/v2/extensions/fwaas/fwaas.go | 2 +- .../v2/extensions/fwaas_v2/fwaas_v2.go | 2 +- auth_options.go | 18 +- doc.go | 7 +- docs/contributor-tutorial/.template/doc.go | 7 +- openstack/baremetal/apiversions/doc.go | 1 - openstack/baremetal/httpbasic/doc.go | 20 +- openstack/baremetal/v1/nodes/results.go | 4 +- openstack/baremetal/v1/ports/doc.go | 110 +++--- openstack/blockstorage/apiversions/doc.go | 1 - .../extensions/availabilityzones/doc.go | 22 +- .../blockstorage/extensions/limits/doc.go | 10 +- .../blockstorage/extensions/quotasets/doc.go | 1 - .../extensions/volumeactions/doc.go | 1 - openstack/blockstorage/v3/attachments/doc.go | 1 - .../blockstorage/v3/attachments/requests.go | 2 +- openstack/blockstorage/v3/qos/doc.go | 2 - openstack/blockstorage/v3/snapshots/doc.go | 1 - openstack/blockstorage/v3/volumetypes/doc.go | 1 - openstack/clustering/v1/clusters/doc.go | 19 +- openstack/clustering/v1/nodes/doc.go | 1 - openstack/clustering/v1/policies/doc.go | 13 +- openstack/clustering/v1/policytypes/doc.go | 34 +- openstack/clustering/v1/profiles/doc.go | 2 - openstack/clustering/v1/profiletypes/doc.go | 2 +- openstack/clustering/v1/receivers/doc.go | 1 - openstack/clustering/v1/webhooks/doc.go | 1 - openstack/common/extensions/doc.go | 1 - .../compute/v2/extensions/aggregates/doc.go | 1 - .../v2/extensions/availabilityzones/doc.go | 44 +-- .../v2/extensions/bootfromvolume/doc.go | 8 +- .../compute/v2/extensions/diagnostics/doc.go | 1 - .../compute/v2/extensions/evacuate/results.go | 4 +- .../extendedserverattributes/doc.go | 94 ++--- .../compute/v2/extensions/keypairs/doc.go | 1 - .../compute/v2/extensions/migrate/doc.go | 1 - .../extensions/quotasets/testing/fixtures.go | 6 +- .../v2/extensions/remoteconsoles/doc.go | 24 +- .../v2/extensions/rescueunrescue/doc.go | 28 +- .../compute/v2/extensions/secgroups/doc.go | 3 +- .../compute/v2/extensions/servergroups/doc.go | 30 +- .../compute/v2/extensions/serverusage/doc.go | 20 +- .../extensions/services/testing/fixtures.go | 2 +- openstack/compute/v2/extensions/tags/doc.go | 74 ++-- openstack/compute/v2/extensions/usage/doc.go | 1 - .../volumeattach/testing/requests_test.go | 2 +- openstack/compute/v2/flavors/requests.go | 20 +- openstack/compute/v2/servers/requests.go | 20 +- openstack/compute/v2/servers/results.go | 3 +- .../v2/servers/testing/results_test.go | 3 +- openstack/containerinfra/v1/clusters/doc.go | 23 +- openstack/containerinfra/v1/nodegroups/doc.go | 152 ++++---- openstack/containerinfra/v1/quotas/doc.go | 1 - openstack/dns/v2/transfer/accept/doc.go | 54 +-- openstack/dns/v2/transfer/request/doc.go | 52 +-- openstack/identity/v3/endpoints/doc.go | 1 - .../v3/extensions/ec2credentials/doc.go | 1 - .../identity/v3/extensions/ec2tokens/doc.go | 1 - .../identity/v3/extensions/oauth1/doc.go | 1 - .../v3/extensions/projectendpoints/doc.go | 1 - .../identity/v3/extensions/trusts/doc.go | 62 ++-- openstack/identity/v3/limits/doc.go | 1 - openstack/identity/v3/services/doc.go | 1 - openstack/identity/v3/tokens/doc.go | 1 - openstack/identity/v3/users/doc.go | 1 - openstack/imageservice/v2/imagedata/doc.go | 24 +- openstack/imageservice/v2/imageimport/doc.go | 28 +- openstack/imageservice/v2/members/requests.go | 22 +- openstack/imageservice/v2/tasks/doc.go | 74 ++-- .../loadbalancer/v2/monitors/requests.go | 18 +- openstack/loadbalancer/v2/pools/results.go | 19 +- openstack/loadbalancer/v2/providers/doc.go | 1 - openstack/loadbalancer/v2/quotas/doc.go | 42 +-- openstack/messaging/v2/queues/doc.go | 28 +- .../v2/extensions/attributestags/doc.go | 14 +- .../v2/extensions/layer3/addressscopes/doc.go | 82 ++--- .../extensions/layer3/portforwarding/doc.go | 1 - .../v2/extensions/lbaas/monitors/requests.go | 2 +- .../v2/extensions/lbaas/vips/results.go | 19 +- .../extensions/lbaas_v2/monitors/requests.go | 18 +- .../v2/extensions/lbaas_v2/pools/results.go | 19 +- .../extensions/networkipavailabilities/doc.go | 32 +- .../v2/extensions/qos/policies/doc.go | 332 +++++++++--------- .../networking/v2/extensions/qos/rules/doc.go | 290 +++++++-------- .../v2/extensions/qos/ruletypes/doc.go | 12 +- .../networking/v2/extensions/quotas/doc.go | 64 ++-- .../v2/extensions/rbacpolicies/doc.go | 23 +- .../networking/v2/extensions/security/doc.go | 22 +- .../v2/extensions/vlantransparent/doc.go | 54 +-- .../v2/extensions/vpnaas/ikepolicies/doc.go | 4 - .../v2/extensions/vpnaas/ipsecpolicies/doc.go | 1 - .../v2/extensions/vpnaas/services/doc.go | 1 - .../extensions/vpnaas/siteconnections/doc.go | 40 +-- openstack/objectstorage/v1/accounts/doc.go | 1 - .../orchestration/v1/resourcetypes/doc.go | 22 +- openstack/orchestration/v1/stackevents/doc.go | 20 +- .../orchestration/v1/stackresources/doc.go | 99 +++--- openstack/orchestration/v1/stacks/doc.go | 186 +++++----- openstack/orchestration/v1/stacks/requests.go | 6 +- .../orchestration/v1/stacktemplates/doc.go | 43 ++- .../placement/v1/resourceproviders/doc.go | 1 - openstack/sharedfilesystems/v2/shares/doc.go | 5 +- openstack/utils/testing/doc.go | 2 +- openstack/workflow/v2/crontriggers/doc.go | 3 +- openstack/workflow/v2/executions/doc.go | 3 +- openstack/workflow/v2/workflows/doc.go | 48 +-- params.go | 46 +-- 108 files changed, 1383 insertions(+), 1420 deletions(-) diff --git a/acceptance/openstack/compute/v2/compute.go b/acceptance/openstack/compute/v2/compute.go index 4ee2c5fde8..21c94192d0 100644 --- a/acceptance/openstack/compute/v2/compute.go +++ b/acceptance/openstack/compute/v2/compute.go @@ -1140,7 +1140,7 @@ func WaitForComputeStatus(client *gophercloud.ServiceClient, server *servers.Ser }) } -//Convenience method to fill an QuotaSet-UpdateOpts-struct from a QuotaSet-struct +// Convenience method to fill an QuotaSet-UpdateOpts-struct from a QuotaSet-struct func FillUpdateOptsFromQuotaSet(src quotasets.QuotaSet, dest *quotasets.UpdateOpts) { dest.FixedIPs = &src.FixedIPs dest.FloatingIPs = &src.FloatingIPs diff --git a/acceptance/openstack/networking/v2/extensions/fwaas/fwaas.go b/acceptance/openstack/networking/v2/extensions/fwaas/fwaas.go index af20a111f1..5f5bde95ff 100644 --- a/acceptance/openstack/networking/v2/extensions/fwaas/fwaas.go +++ b/acceptance/openstack/networking/v2/extensions/fwaas/fwaas.go @@ -117,7 +117,7 @@ func CreatePolicy(t *testing.T, client *gophercloud.ServiceClient, ruleID string } // CreateRule will create a Firewall Rule with a random source address and -//source port, destination address and port. An error will be returned if +// source port, destination address and port. An error will be returned if // the rule could not be created. func CreateRule(t *testing.T, client *gophercloud.ServiceClient) (*rules.Rule, error) { ruleName := tools.RandomString("TESTACC-", 8) diff --git a/acceptance/openstack/networking/v2/extensions/fwaas_v2/fwaas_v2.go b/acceptance/openstack/networking/v2/extensions/fwaas_v2/fwaas_v2.go index 1b225691ff..57a621a8bd 100644 --- a/acceptance/openstack/networking/v2/extensions/fwaas_v2/fwaas_v2.go +++ b/acceptance/openstack/networking/v2/extensions/fwaas_v2/fwaas_v2.go @@ -69,7 +69,7 @@ func CreatePolicy(t *testing.T, client *gophercloud.ServiceClient, ruleID string } // CreateRule will create a Firewall Rule with a random source address and -//source port, destination address and port. An error will be returned if +// source port, destination address and port. An error will be returned if // the rule could not be created. func CreateRule(t *testing.T, client *gophercloud.ServiceClient) (*rules.Rule, error) { ruleName := tools.RandomString("TESTACC-", 8) diff --git a/auth_options.go b/auth_options.go index 4f301305e6..335ce87957 100644 --- a/auth_options.go +++ b/auth_options.go @@ -12,20 +12,20 @@ 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}", - } + opts := gophercloud.AuthOptions{ + IdentityEndpoint: "https://openstack.example.com:5000/v2.0", + Username: "{username}", + Password: "{password}", + TenantID: "{tenant_id}", + } - provider, err := openstack.AuthenticatedClient(opts) + provider, err := openstack.AuthenticatedClient(opts) An example of using AuthOptionsFromEnv(), where the environment variables can be read from a file, such as a standard openrc file: - opts, err := openstack.AuthOptionsFromEnv() - provider, err := openstack.AuthenticatedClient(opts) + opts, err := openstack.AuthOptionsFromEnv() + provider, err := openstack.AuthenticatedClient(opts) */ type AuthOptions struct { // IdentityEndpoint specifies the HTTP endpoint that is required to work with diff --git a/doc.go b/doc.go index e2623b44e2..19b64d6508 100644 --- a/doc.go +++ b/doc.go @@ -3,7 +3,7 @@ Package gophercloud provides a multi-vendor interface to OpenStack-compatible clouds. The library has a three-level hierarchy: providers, services, and resources. -Authenticating with Providers +# Authenticating with Providers Provider structs represent the cloud providers that offer and manage a collection of services. You will generally want to create one Provider @@ -49,7 +49,7 @@ instead of "project". opts, err := openstack.AuthOptionsFromEnv() provider, err := openstack.AuthenticatedClient(opts) -Service Clients +# Service Clients Service structs are specific to a provider and handle all of the logic and operations for a particular OpenStack service. Examples of services include: @@ -60,7 +60,7 @@ pass in the parent provider, like so: client, err := openstack.NewComputeV2(provider, opts) -Resources +# Resources Resource structs are the domain models that services make use of in order to work with and represent the state of API resources: @@ -144,6 +144,5 @@ An example retry backoff function, which respects the 429 HTTP response code and return nil } - */ package gophercloud diff --git a/docs/contributor-tutorial/.template/doc.go b/docs/contributor-tutorial/.template/doc.go index bbf39c5428..096bfa20b4 100644 --- a/docs/contributor-tutorial/.template/doc.go +++ b/docs/contributor-tutorial/.template/doc.go @@ -1,13 +1,12 @@ /* Package NAME manages and retrieves RESOURCE in the OpenStack SERVICE Service. -Example to List RESOURCE +# Example to List RESOURCE -Example to Create a RESOURCE +# Example to Create a RESOURCE -Example to Update a RESOURCE +# Example to Update a RESOURCE Example to Delete a RESOURCE - */ package RESOURCE diff --git a/openstack/baremetal/apiversions/doc.go b/openstack/baremetal/apiversions/doc.go index 7fbbac0d80..db8c62b974 100644 --- a/openstack/baremetal/apiversions/doc.go +++ b/openstack/baremetal/apiversions/doc.go @@ -18,6 +18,5 @@ Package apiversions provides information about the versions supported by a speci if err != nil { panic("unable to get API version: " + err.Error()) } - */ package apiversions diff --git a/openstack/baremetal/httpbasic/doc.go b/openstack/baremetal/httpbasic/doc.go index d36e9f71cd..ab8618b40d 100644 --- a/openstack/baremetal/httpbasic/doc.go +++ b/openstack/baremetal/httpbasic/doc.go @@ -3,16 +3,16 @@ Package httpbasic provides support for http_basic bare metal endpoints. Example of obtaining and using a client: - client, err := httpbasic.NewBareMetalHTTPBasic(httpbasic.Endpoints{ - IronicEndpoing: "http://localhost:6385/v1/", - IronicUser: "myUser", - IronicUserPassword: "myPassword", - }) - if err != nil { - panic(err) - } + client, err := httpbasic.NewBareMetalHTTPBasic(httpbasic.Endpoints{ + IronicEndpoing: "http://localhost:6385/v1/", + IronicUser: "myUser", + IronicUserPassword: "myPassword", + }) + if err != nil { + panic(err) + } - client.Microversion = "1.50" - nodes.ListDetail(client, nodes.listOpts{}) + client.Microversion = "1.50" + nodes.ListDetail(client, nodes.listOpts{}) */ package httpbasic diff --git a/openstack/baremetal/v1/nodes/results.go b/openstack/baremetal/v1/nodes/results.go index f07b1be54a..2f3d39f3d8 100644 --- a/openstack/baremetal/v1/nodes/results.go +++ b/openstack/baremetal/v1/nodes/results.go @@ -373,8 +373,8 @@ type DriverValidation struct { Reason string `json:"reason"` } -// Ironic validates whether the Node’s driver has enough information to manage the Node. This polls each interface on -// the driver, and returns the status of that interface as an DriverValidation struct. +// Ironic validates whether the Node’s driver has enough information to manage the Node. This polls each interface on +// the driver, and returns the status of that interface as an DriverValidation struct. type NodeValidation struct { BIOS DriverValidation `json:"bios"` Boot DriverValidation `json:"boot"` diff --git a/openstack/baremetal/v1/ports/doc.go b/openstack/baremetal/v1/ports/doc.go index eb0579bed5..46805dd349 100644 --- a/openstack/baremetal/v1/ports/doc.go +++ b/openstack/baremetal/v1/ports/doc.go @@ -1,85 +1,83 @@ /* - Package ports contains the functionality to Listing, Searching, Creating, Updating, - and Deleting of bare metal Port resources - - API reference: https://developer.openstack.org/api-ref/baremetal/#ports-ports + Package ports contains the functionality to Listing, Searching, Creating, Updating, + and Deleting of bare metal Port resources + API reference: https://developer.openstack.org/api-ref/baremetal/#ports-ports Example to List Ports with Detail - ports.ListDetail(client, nil).EachPage(func(page pagination.Page) (bool, error) { - portList, err := ports.ExtractPorts(page) - if err != nil { - return false, err - } + ports.ListDetail(client, nil).EachPage(func(page pagination.Page) (bool, error) { + portList, err := ports.ExtractPorts(page) + if err != nil { + return false, err + } - for _, n := range portList { - // Do something - } + for _, n := range portList { + // Do something + } - return true, nil - }) + return true, nil + }) Example to List Ports - listOpts := ports.ListOpts{ - Limit: 10, - } + listOpts := ports.ListOpts{ + Limit: 10, + } - ports.List(client, listOpts).EachPage(func(page pagination.Page) (bool, error) { - portList, err := ports.ExtractPorts(page) - if err != nil { - return false, err - } + ports.List(client, listOpts).EachPage(func(page pagination.Page) (bool, error) { + portList, err := ports.ExtractPorts(page) + if err != nil { + return false, err + } - for _, n := range portList { - // Do something - } + for _, n := range portList { + // Do something + } - return true, nil - }) + return true, nil + }) Example to Create a Port - createOpts := ports.CreateOpts{ - NodeUUID: "e8920409-e07e-41bb-8cc1-72acb103e2dd", - Address: "00:1B:63:84:45:E6", - PhysicalNetwork: "my-network", - } + createOpts := ports.CreateOpts{ + NodeUUID: "e8920409-e07e-41bb-8cc1-72acb103e2dd", + Address: "00:1B:63:84:45:E6", + PhysicalNetwork: "my-network", + } - createPort, err := ports.Create(client, createOpts).Extract() - if err != nil { - panic(err) - } + createPort, err := ports.Create(client, createOpts).Extract() + if err != nil { + panic(err) + } Example to Get a Port - showPort, err := ports.Get(client, "c9afd385-5d89-4ecb-9e1c-68194da6b474").Extract() - if err != nil { - panic(err) - } + showPort, err := ports.Get(client, "c9afd385-5d89-4ecb-9e1c-68194da6b474").Extract() + if err != nil { + panic(err) + } Example to Update a Port - updateOpts := ports.UpdateOpts{ - ports.UpdateOperation{ - Op: ReplaceOp, - Path: "/address", - Value: "22:22:22:22:22:22", - }, - } + updateOpts := ports.UpdateOpts{ + ports.UpdateOperation{ + Op: ReplaceOp, + Path: "/address", + Value: "22:22:22:22:22:22", + }, + } - updatePort, err := ports.Update(client, "c9afd385-5d89-4ecb-9e1c-68194da6b474", updateOpts).Extract() - if err != nil { - panic(err) - } + updatePort, err := ports.Update(client, "c9afd385-5d89-4ecb-9e1c-68194da6b474", updateOpts).Extract() + if err != nil { + panic(err) + } Example to Delete a Port - err = ports.Delete(client, "c9afd385-5d89-4ecb-9e1c-68194da6b474").ExtractErr() - if err != nil { - panic(err) - } - + err = ports.Delete(client, "c9afd385-5d89-4ecb-9e1c-68194da6b474").ExtractErr() + if err != nil { + panic(err) + } */ package ports diff --git a/openstack/blockstorage/apiversions/doc.go b/openstack/blockstorage/apiversions/doc.go index 8c38b506bf..7f05463618 100644 --- a/openstack/blockstorage/apiversions/doc.go +++ b/openstack/blockstorage/apiversions/doc.go @@ -18,7 +18,6 @@ Example of Retrieving all API Versions fmt.Printf("%+v\n", version) } - Example of Retrieving an API Version version, err := apiversions.Get(client, "v3").Extract() diff --git a/openstack/blockstorage/extensions/availabilityzones/doc.go b/openstack/blockstorage/extensions/availabilityzones/doc.go index 0b9a5a6b58..29faa8dcbc 100644 --- a/openstack/blockstorage/extensions/availabilityzones/doc.go +++ b/openstack/blockstorage/extensions/availabilityzones/doc.go @@ -4,18 +4,18 @@ available volume availability zones. Example of Get Availability Zone Information - allPages, err := availabilityzones.List(volumeClient).AllPages() - if err != nil { - panic(err) - } + allPages, err := availabilityzones.List(volumeClient).AllPages() + if err != nil { + panic(err) + } - availabilityZoneInfo, err := availabilityzones.ExtractAvailabilityZones(allPages) - if err != nil { - panic(err) - } + availabilityZoneInfo, err := availabilityzones.ExtractAvailabilityZones(allPages) + if err != nil { + panic(err) + } - for _, zoneInfo := range availabilityZoneInfo { - fmt.Printf("%+v\n", zoneInfo) - } + for _, zoneInfo := range availabilityZoneInfo { + fmt.Printf("%+v\n", zoneInfo) + } */ package availabilityzones diff --git a/openstack/blockstorage/extensions/limits/doc.go b/openstack/blockstorage/extensions/limits/doc.go index bb19f80305..d331058847 100644 --- a/openstack/blockstorage/extensions/limits/doc.go +++ b/openstack/blockstorage/extensions/limits/doc.go @@ -3,11 +3,11 @@ Package limits shows rate and limit information for a project you authorized for Example to Retrieve Limits - limits, err := limits.Get(blockStorageClient).Extract() - if err != nil { - panic(err) - } + limits, err := limits.Get(blockStorageClient).Extract() + if err != nil { + panic(err) + } - fmt.Printf("%+v\n", limits) + fmt.Printf("%+v\n", limits) */ package limits diff --git a/openstack/blockstorage/extensions/quotasets/doc.go b/openstack/blockstorage/extensions/quotasets/doc.go index 0f9c9d4831..a60f953d0b 100644 --- a/openstack/blockstorage/extensions/quotasets/doc.go +++ b/openstack/blockstorage/extensions/quotasets/doc.go @@ -50,7 +50,6 @@ Example to Update a Quota set with volume_type quotas fmt.Printf("%+v\n", quotaset) - Example to Delete a Quota Set err := quotasets.Delete(blockStorageClient, "project-id").ExtractErr() diff --git a/openstack/blockstorage/extensions/volumeactions/doc.go b/openstack/blockstorage/extensions/volumeactions/doc.go index 69d803d05c..34db834f7c 100644 --- a/openstack/blockstorage/extensions/volumeactions/doc.go +++ b/openstack/blockstorage/extensions/volumeactions/doc.go @@ -25,7 +25,6 @@ Example of Attaching a Volume to an Instance panic(err) } - Example of Creating an Image from a Volume uploadImageOpts := volumeactions.UploadImageOpts{ diff --git a/openstack/blockstorage/v3/attachments/doc.go b/openstack/blockstorage/v3/attachments/doc.go index dc6156ac74..b3962d37f5 100644 --- a/openstack/blockstorage/v3/attachments/doc.go +++ b/openstack/blockstorage/v3/attachments/doc.go @@ -82,6 +82,5 @@ Example to Delete Attachment if err != nil { panic(err) } - */ package attachments diff --git a/openstack/blockstorage/v3/attachments/requests.go b/openstack/blockstorage/v3/attachments/requests.go index 3feba700e4..b6032a3b0d 100644 --- a/openstack/blockstorage/v3/attachments/requests.go +++ b/openstack/blockstorage/v3/attachments/requests.go @@ -12,7 +12,7 @@ type CreateOptsBuilder interface { } // CreateOpts contains options for creating a Volume attachment. This object is -//passed to the Create function. For more information about these parameters, +// passed to the Create function. For more information about these parameters, // see the Attachment object. type CreateOpts struct { // VolumeUUID is the UUID of the Cinder volume to create the attachment diff --git a/openstack/blockstorage/v3/qos/doc.go b/openstack/blockstorage/v3/qos/doc.go index 0a6008159a..86f288fa5d 100644 --- a/openstack/blockstorage/v3/qos/doc.go +++ b/openstack/blockstorage/v3/qos/doc.go @@ -78,7 +78,6 @@ Example of updating QoSSpec } fmt.Printf("%+v\n", specs) - Example of deleting specific keys/specs from a QoS qosID := "de075d5e-8afc-4e23-9388-b84a5183d1c0" @@ -143,6 +142,5 @@ Example of listing all associations of a QoS for _, association := range allAssociations { fmt.Printf("Association: %+v\n", association) } - */ package qos diff --git a/openstack/blockstorage/v3/snapshots/doc.go b/openstack/blockstorage/v3/snapshots/doc.go index 76960f4218..702094c3df 100644 --- a/openstack/blockstorage/v3/snapshots/doc.go +++ b/openstack/blockstorage/v3/snapshots/doc.go @@ -4,7 +4,6 @@ OpenStack Block Storage service. A snapshot is a point in time copy of the data contained in an external storage volume, and can be controlled programmatically. - Example to list Snapshots allPages, err := snapshots.List(client, snapshots.ListOpts{}).AllPages() diff --git a/openstack/blockstorage/v3/volumetypes/doc.go b/openstack/blockstorage/v3/volumetypes/doc.go index 4e48e9c022..55a2170bc2 100644 --- a/openstack/blockstorage/v3/volumetypes/doc.go +++ b/openstack/blockstorage/v3/volumetypes/doc.go @@ -160,6 +160,5 @@ Example to Remove/Revoke Access to a Volume Type if err != nil { panic(err) } - */ package volumetypes diff --git a/openstack/clustering/v1/clusters/doc.go b/openstack/clustering/v1/clusters/doc.go index 7d28ac5945..b25dede0fe 100644 --- a/openstack/clustering/v1/clusters/doc.go +++ b/openstack/clustering/v1/clusters/doc.go @@ -225,15 +225,15 @@ Example to Complete Life Cycle Example to add nodes to a cluster - addNodesOpts := clusters.AddNodesOpts{ - Nodes: []string{"node-123"}, - } - clusterID := "b7b870e3-d3c5-4a93-b9d7-846c53b2c2da" - actionID, err := clusters.AddNodes(serviceClient, clusterID, addNodesOpts).Extract() - if err != nil { - panic(err) - } - fmt.Println("action=", actionID) + addNodesOpts := clusters.AddNodesOpts{ + Nodes: []string{"node-123"}, + } + clusterID := "b7b870e3-d3c5-4a93-b9d7-846c53b2c2da" + actionID, err := clusters.AddNodes(serviceClient, clusterID, addNodesOpts).Extract() + if err != nil { + panic(err) + } + fmt.Println("action=", actionID) Example to remove nodes from a cluster @@ -282,6 +282,5 @@ Example to perform an operation on a cluster if err != nil { panic(err) } - */ package clusters diff --git a/openstack/clustering/v1/nodes/doc.go b/openstack/clustering/v1/nodes/doc.go index 3d7ce0b805..230cd917cd 100644 --- a/openstack/clustering/v1/nodes/doc.go +++ b/openstack/clustering/v1/nodes/doc.go @@ -106,6 +106,5 @@ Example to Check a Node panic(err) } fmt.Println("action=", actionID) - */ package nodes diff --git a/openstack/clustering/v1/policies/doc.go b/openstack/clustering/v1/policies/doc.go index 422f5cf9f0..7424bc8982 100644 --- a/openstack/clustering/v1/policies/doc.go +++ b/openstack/clustering/v1/policies/doc.go @@ -22,7 +22,6 @@ Example to List Policies fmt.Printf("%+v\n", policy) } - Example to Create a Policy createOpts := policies.CreateOpts{ @@ -50,13 +49,13 @@ Example to Create a Policy Example to Get a Policy - policyName := "get_policy" - policyDetail, err := policies.Get(clusteringClient, policyName).Extract() - if err != nil { - panic(err) - } + policyName := "get_policy" + policyDetail, err := policies.Get(clusteringClient, policyName).Extract() + if err != nil { + panic(err) + } - fmt.Printf("%+v\n", policyDetail) + fmt.Printf("%+v\n", policyDetail) Example to Update a Policy diff --git a/openstack/clustering/v1/policytypes/doc.go b/openstack/clustering/v1/policytypes/doc.go index 2b1b6d6860..c8617d6d8e 100644 --- a/openstack/clustering/v1/policytypes/doc.go +++ b/openstack/clustering/v1/policytypes/doc.go @@ -4,28 +4,28 @@ from the OpenStack Clustering Service. Example to List Policy Types - allPages, err := policytypes.List(clusteringClient).AllPages() - if err != nil { - panic(err) - } + allPages, err := policytypes.List(clusteringClient).AllPages() + if err != nil { + panic(err) + } - allPolicyTypes, err := actions.ExtractPolicyTypes(allPages) - if err != nil { - panic(err) - } + allPolicyTypes, err := actions.ExtractPolicyTypes(allPages) + if err != nil { + panic(err) + } - for _, policyType := range allPolicyTypes { - fmt.Printf("%+v\n", policyType) - } + for _, policyType := range allPolicyTypes { + fmt.Printf("%+v\n", policyType) + } Example to Get a Policy Type - policyTypeName := "senlin.policy.affinity-1.0" - policyTypeDetail, err := policyTypes.Get(clusteringClient, policyTypeName).Extract() - if err != nil { - panic(err) - } + policyTypeName := "senlin.policy.affinity-1.0" + policyTypeDetail, err := policyTypes.Get(clusteringClient, policyTypeName).Extract() + if err != nil { + panic(err) + } - fmt.Printf("%+v\n", policyTypeDetail) + fmt.Printf("%+v\n", policyTypeDetail) */ package policytypes diff --git a/openstack/clustering/v1/profiles/doc.go b/openstack/clustering/v1/profiles/doc.go index 4e27eb8861..1b8348a379 100644 --- a/openstack/clustering/v1/profiles/doc.go +++ b/openstack/clustering/v1/profiles/doc.go @@ -41,7 +41,6 @@ Example to Get a Profile fmt.Print("profile", profile) - Example to List Profiles listOpts := profiles.ListOpts{ @@ -105,6 +104,5 @@ Example to Validate a profile if err != nil { panic(err) } - */ package profiles diff --git a/openstack/clustering/v1/profiletypes/doc.go b/openstack/clustering/v1/profiletypes/doc.go index 64f1fc99bc..8c95abfe08 100644 --- a/openstack/clustering/v1/profiletypes/doc.go +++ b/openstack/clustering/v1/profiletypes/doc.go @@ -26,6 +26,7 @@ Example to Get a ProfileType fmt.Printf("%+v\n", profileType) Example of list operations supported by a profile type + serviceClient.Microversion = "1.5" profileTypeName := "os.nova.server-1.0" @@ -42,6 +43,5 @@ Example of list operations supported by a profile type for _, op := range ops { fmt.Printf("%+v\n", op) } - */ package profiletypes diff --git a/openstack/clustering/v1/receivers/doc.go b/openstack/clustering/v1/receivers/doc.go index 92506c856b..72f7dc2285 100644 --- a/openstack/clustering/v1/receivers/doc.go +++ b/openstack/clustering/v1/receivers/doc.go @@ -76,6 +76,5 @@ Example to Notify a Receiver if err != nil { panic(err) } - */ package receivers diff --git a/openstack/clustering/v1/webhooks/doc.go b/openstack/clustering/v1/webhooks/doc.go index c76dc11ffb..a96e0d2884 100644 --- a/openstack/clustering/v1/webhooks/doc.go +++ b/openstack/clustering/v1/webhooks/doc.go @@ -10,6 +10,5 @@ Example to Trigger webhook action } fmt.Println("result", result) - */ package webhooks diff --git a/openstack/common/extensions/doc.go b/openstack/common/extensions/doc.go index b3d3436698..a67542cac0 100644 --- a/openstack/common/extensions/doc.go +++ b/openstack/common/extensions/doc.go @@ -33,7 +33,6 @@ Example of Retrieving Compute Extensions fmt.Printf("%+v\n", extension) } - Example of Retrieving Network Extensions ao, err := openstack.AuthOptionsFromEnv() diff --git a/openstack/compute/v2/extensions/aggregates/doc.go b/openstack/compute/v2/extensions/aggregates/doc.go index fbbf182b57..e1e2519d54 100644 --- a/openstack/compute/v2/extensions/aggregates/doc.go +++ b/openstack/compute/v2/extensions/aggregates/doc.go @@ -100,6 +100,5 @@ Example of Create or Update Metadata panic(err) } fmt.Printf("%+v\n", aggregate) - */ package aggregates diff --git a/openstack/compute/v2/extensions/availabilityzones/doc.go b/openstack/compute/v2/extensions/availabilityzones/doc.go index 29b554d213..13b6d9d424 100644 --- a/openstack/compute/v2/extensions/availabilityzones/doc.go +++ b/openstack/compute/v2/extensions/availabilityzones/doc.go @@ -28,34 +28,34 @@ Example of Extend server result with Availability Zone Information: Example of Get Availability Zone Information - allPages, err := availabilityzones.List(computeClient).AllPages() - if err != nil { - panic(err) - } + allPages, err := availabilityzones.List(computeClient).AllPages() + if err != nil { + panic(err) + } - availabilityZoneInfo, err := availabilityzones.ExtractAvailabilityZones(allPages) - if err != nil { - panic(err) - } + availabilityZoneInfo, err := availabilityzones.ExtractAvailabilityZones(allPages) + if err != nil { + panic(err) + } - for _, zoneInfo := range availabilityZoneInfo { - fmt.Printf("%+v\n", zoneInfo) - } + for _, zoneInfo := range availabilityZoneInfo { + fmt.Printf("%+v\n", zoneInfo) + } Example of Get Detailed Availability Zone Information - allPages, err := availabilityzones.ListDetail(computeClient).AllPages() - if err != nil { - panic(err) - } + allPages, err := availabilityzones.ListDetail(computeClient).AllPages() + if err != nil { + panic(err) + } - availabilityZoneInfo, err := availabilityzones.ExtractAvailabilityZones(allPages) - if err != nil { - panic(err) - } + availabilityZoneInfo, err := availabilityzones.ExtractAvailabilityZones(allPages) + if err != nil { + panic(err) + } - for _, zoneInfo := range availabilityZoneInfo { - fmt.Printf("%+v\n", zoneInfo) - } + for _, zoneInfo := range availabilityZoneInfo { + fmt.Printf("%+v\n", zoneInfo) + } */ package availabilityzones diff --git a/openstack/compute/v2/extensions/bootfromvolume/doc.go b/openstack/compute/v2/extensions/bootfromvolume/doc.go index d291325e0a..79a09b33cf 100644 --- a/openstack/compute/v2/extensions/bootfromvolume/doc.go +++ b/openstack/compute/v2/extensions/bootfromvolume/doc.go @@ -10,7 +10,7 @@ https://docs.openstack.org/nova/latest/user/block-device-mapping.html Note that this package implements `block_device_mapping_v2`. -Example of Creating a Server From an Image +# Example of Creating a Server From an Image This example will boot a server from an image and use a standard ephemeral disk as the server's root disk. This is virtually no different than creating @@ -42,7 +42,7 @@ a server without using block device mappings. panic(err) } -Example of Creating a Server From a New Volume +# Example of Creating a Server From a New Volume This example will create a block storage volume based on the given Image. The server will use this volume as its root disk. @@ -72,7 +72,7 @@ server will use this volume as its root disk. panic(err) } -Example of Creating a Server From an Existing Volume +# Example of Creating a Server From an Existing Volume This example will create a server with an existing volume as its root disk. @@ -100,7 +100,7 @@ This example will create a server with an existing volume as its root disk. panic(err) } -Example of Creating a Server with Multiple Ephemeral Disks +# Example of Creating a Server with Multiple Ephemeral Disks This example will create a server with multiple ephemeral disks. The first block device will be based off of an existing Image. Each additional diff --git a/openstack/compute/v2/extensions/diagnostics/doc.go b/openstack/compute/v2/extensions/diagnostics/doc.go index 8141120c3e..8fdfc23e35 100644 --- a/openstack/compute/v2/extensions/diagnostics/doc.go +++ b/openstack/compute/v2/extensions/diagnostics/doc.go @@ -9,6 +9,5 @@ Example of Show Diagnostics } fmt.Printf("%+v\n", diags) - */ package diagnostics diff --git a/openstack/compute/v2/extensions/evacuate/results.go b/openstack/compute/v2/extensions/evacuate/results.go index 8342cb43d0..54eacfc580 100644 --- a/openstack/compute/v2/extensions/evacuate/results.go +++ b/openstack/compute/v2/extensions/evacuate/results.go @@ -5,8 +5,8 @@ import ( ) // 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.. +// 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 } diff --git a/openstack/compute/v2/extensions/extendedserverattributes/doc.go b/openstack/compute/v2/extensions/extendedserverattributes/doc.go index 53e24dbe1b..71a9609e09 100644 --- a/openstack/compute/v2/extensions/extendedserverattributes/doc.go +++ b/openstack/compute/v2/extensions/extendedserverattributes/doc.go @@ -4,64 +4,64 @@ server result with the extended usage information. Example to Get basic extended information: - type serverAttributesExt struct { - servers.Server - extendedserverattributes.ServerAttributesExt - } - var serverWithAttributesExt serverAttributesExt + type serverAttributesExt struct { + servers.Server + extendedserverattributes.ServerAttributesExt + } + var serverWithAttributesExt serverAttributesExt - err := servers.Get(computeClient, "d650a0ce-17c3-497d-961a-43c4af80998a").ExtractInto(&serverWithAttributesExt) - if err != nil { - panic(err) - } + err := servers.Get(computeClient, "d650a0ce-17c3-497d-961a-43c4af80998a").ExtractInto(&serverWithAttributesExt) + if err != nil { + panic(err) + } - fmt.Printf("%+v\n", serverWithAttributesExt) + fmt.Printf("%+v\n", serverWithAttributesExt) Example to get additional fields with microversion 2.3 or later - computeClient.Microversion = "2.3" - result := servers.Get(computeClient, "d650a0ce-17c3-497d-961a-43c4af80998a") + computeClient.Microversion = "2.3" + result := servers.Get(computeClient, "d650a0ce-17c3-497d-961a-43c4af80998a") - reservationID, err := extendedserverattributes.ExtractReservationID(result.Result) - if err != nil { - panic(err) - } - fmt.Printf("%s\n", reservationID) + reservationID, err := extendedserverattributes.ExtractReservationID(result.Result) + if err != nil { + panic(err) + } + fmt.Printf("%s\n", reservationID) - launchIndex, err := extendedserverattributes.ExtractLaunchIndex(result.Result) - if err != nil { - panic(err) - } - fmt.Printf("%d\n", launchIndex) + launchIndex, err := extendedserverattributes.ExtractLaunchIndex(result.Result) + if err != nil { + panic(err) + } + fmt.Printf("%d\n", launchIndex) - ramdiskID, err := extendedserverattributes.ExtractRamdiskID(result.Result) - if err != nil { - panic(err) - } - fmt.Printf("%s\n", ramdiskID) + ramdiskID, err := extendedserverattributes.ExtractRamdiskID(result.Result) + if err != nil { + panic(err) + } + fmt.Printf("%s\n", ramdiskID) - kernelID, err := extendedserverattributes.ExtractKernelID(result.Result) - if err != nil { - panic(err) - } - fmt.Printf("%s\n", kernelID) + kernelID, err := extendedserverattributes.ExtractKernelID(result.Result) + if err != nil { + panic(err) + } + fmt.Printf("%s\n", kernelID) - hostname, err := extendedserverattributes.ExtractHostname(result.Result) - if err != nil { - panic(err) - } - fmt.Printf("%s\n", hostname) + hostname, err := extendedserverattributes.ExtractHostname(result.Result) + if err != nil { + panic(err) + } + fmt.Printf("%s\n", hostname) - rootDeviceName, err := extendedserverattributes.ExtractRootDeviceName(result.Result) - if err != nil { - panic(err) - } - fmt.Printf("%s\n", rootDeviceName) + rootDeviceName, err := extendedserverattributes.ExtractRootDeviceName(result.Result) + if err != nil { + panic(err) + } + fmt.Printf("%s\n", rootDeviceName) - userData, err := extendedserverattributes.ExtractUserData(result.Result) - if err != nil { - panic(err) - } - fmt.Printf("%s\n", userData) + userData, err := extendedserverattributes.ExtractUserData(result.Result) + if err != nil { + panic(err) + } + fmt.Printf("%s\n", userData) */ package extendedserverattributes diff --git a/openstack/compute/v2/extensions/keypairs/doc.go b/openstack/compute/v2/extensions/keypairs/doc.go index 15173bd76b..9fa914ec71 100644 --- a/openstack/compute/v2/extensions/keypairs/doc.go +++ b/openstack/compute/v2/extensions/keypairs/doc.go @@ -115,6 +115,5 @@ Example to Get a Key Pair owned by a certain user using microversion 2.10 or gre if err != nil { panic(err) } - */ package keypairs diff --git a/openstack/compute/v2/extensions/migrate/doc.go b/openstack/compute/v2/extensions/migrate/doc.go index cf3067716d..f0c3291d04 100644 --- a/openstack/compute/v2/extensions/migrate/doc.go +++ b/openstack/compute/v2/extensions/migrate/doc.go @@ -25,6 +25,5 @@ Example of Live-Migrate Server (os-migrateLive Action) if err != nil { panic(err) } - */ package migrate diff --git a/openstack/compute/v2/extensions/quotasets/testing/fixtures.go b/openstack/compute/v2/extensions/quotasets/testing/fixtures.go index c0955c5ca9..03bc83cc80 100644 --- a/openstack/compute/v2/extensions/quotasets/testing/fixtures.go +++ b/openstack/compute/v2/extensions/quotasets/testing/fixtures.go @@ -136,13 +136,13 @@ var FirstQuotaDetailsSet = quotasets.QuotaDetailSet{ ServerGroupMembers: quotasets.QuotaDetail{InUse: 0, Reserved: 0, Limit: 3}, } -//The expected update Body. Is also returned by PUT request +// The expected update Body. Is also returned by PUT request const UpdateOutput = `{"quota_set":{"cores":200,"fixed_ips":0,"floating_ips":0,"injected_file_content_bytes":10240,"injected_file_path_bytes":255,"injected_files":5,"instances":25,"key_pairs":10,"metadata_items":128,"ram":9216000,"security_group_rules":20,"security_groups":10,"server_groups":2,"server_group_members":3}}` -//The expected partialupdate Body. Is also returned by PUT request +// The expected partialupdate Body. Is also returned by PUT request const PartialUpdateBody = `{"quota_set":{"cores":200, "force":true}}` -//Result of Quota-update +// Result of Quota-update var UpdatedQuotaSet = quotasets.UpdateOpts{ FixedIPs: gophercloud.IntToPointer(0), FloatingIPs: gophercloud.IntToPointer(0), diff --git a/openstack/compute/v2/extensions/remoteconsoles/doc.go b/openstack/compute/v2/extensions/remoteconsoles/doc.go index 1ab269b47e..54b281e4b7 100644 --- a/openstack/compute/v2/extensions/remoteconsoles/doc.go +++ b/openstack/compute/v2/extensions/remoteconsoles/doc.go @@ -6,20 +6,20 @@ that API. Example of Creating a new RemoteConsole - computeClient, err := openstack.NewComputeV2(providerClient, endpointOptions) - computeClient.Microversion = "2.6" + computeClient, err := openstack.NewComputeV2(providerClient, endpointOptions) + computeClient.Microversion = "2.6" - createOpts := remoteconsoles.CreateOpts{ - Protocol: remoteconsoles.ConsoleProtocolVNC, - Type: remoteconsoles.ConsoleTypeNoVNC, - } - serverID := "b16ba811-199d-4ffd-8839-ba96c1185a67" + createOpts := remoteconsoles.CreateOpts{ + Protocol: remoteconsoles.ConsoleProtocolVNC, + Type: remoteconsoles.ConsoleTypeNoVNC, + } + serverID := "b16ba811-199d-4ffd-8839-ba96c1185a67" - remtoteConsole, err := remoteconsoles.Create(computeClient, serverID, createOpts).Extract() - if err != nil { - panic(err) - } + remtoteConsole, err := remoteconsoles.Create(computeClient, serverID, createOpts).Extract() + if err != nil { + panic(err) + } - fmt.Printf("Console URL: %s\n", remtoteConsole.URL) + fmt.Printf("Console URL: %s\n", remtoteConsole.URL) */ package remoteconsoles diff --git a/openstack/compute/v2/extensions/rescueunrescue/doc.go b/openstack/compute/v2/extensions/rescueunrescue/doc.go index 2081018cdb..e448efc5ed 100644 --- a/openstack/compute/v2/extensions/rescueunrescue/doc.go +++ b/openstack/compute/v2/extensions/rescueunrescue/doc.go @@ -4,25 +4,25 @@ and to return it back. Example to Rescue a server - rescueOpts := rescueunrescue.RescueOpts{ - AdminPass: "aUPtawPzE9NU", - RescueImageRef: "115e5c5b-72f0-4a0a-9067-60706545248c", - } - serverID := "3f54d05f-3430-4d80-aa07-63e6af9e2488" + rescueOpts := rescueunrescue.RescueOpts{ + AdminPass: "aUPtawPzE9NU", + RescueImageRef: "115e5c5b-72f0-4a0a-9067-60706545248c", + } + serverID := "3f54d05f-3430-4d80-aa07-63e6af9e2488" - adminPass, err := rescueunrescue.Rescue(computeClient, serverID, rescueOpts).Extract() - if err != nil { - panic(err) - } + adminPass, err := rescueunrescue.Rescue(computeClient, serverID, rescueOpts).Extract() + if err != nil { + panic(err) + } - fmt.Printf("adminPass of the rescued server %s: %s\n", serverID, adminPass) + fmt.Printf("adminPass of the rescued server %s: %s\n", serverID, adminPass) Example to Unrescue a server - serverID := "3f54d05f-3430-4d80-aa07-63e6af9e2488" + serverID := "3f54d05f-3430-4d80-aa07-63e6af9e2488" - if err := rescueunrescue.Unrescue(computeClient, serverID).ExtractErr(); err != nil { - panic(err) - } + if err := rescueunrescue.Unrescue(computeClient, serverID).ExtractErr(); err != nil { + panic(err) + } */ package rescueunrescue diff --git a/openstack/compute/v2/extensions/secgroups/doc.go b/openstack/compute/v2/extensions/secgroups/doc.go index 8d3ebf2e5d..eedabaf05e 100644 --- a/openstack/compute/v2/extensions/secgroups/doc.go +++ b/openstack/compute/v2/extensions/secgroups/doc.go @@ -92,8 +92,7 @@ Example to Remove a Security Group from a Server panic(err) } -Example to Delete a Security Group - +# Example to Delete a Security Group sgID := "37d94f8a-d136-465c-ae46-144f0d8ef141" err := secgroups.Delete(computeClient, sgID).ExtractErr() diff --git a/openstack/compute/v2/extensions/servergroups/doc.go b/openstack/compute/v2/extensions/servergroups/doc.go index 936674b051..23fed3c8af 100644 --- a/openstack/compute/v2/extensions/servergroups/doc.go +++ b/openstack/compute/v2/extensions/servergroups/doc.go @@ -31,21 +31,21 @@ Example to Create a Server Group Example to Create a Server Group with additional microversion 2.64 fields - createOpts := servergroups.CreateOpts{ - Name: "my_sg", - Policy: "anti-affinity", - Rules: &servergroups.Rules{ - MaxServerPerHost: 3, - }, - } - - computeClient.Microversion = "2.64" - result := servergroups.Create(computeClient, createOpts) - - serverGroup, err := result.Extract() - if err != nil { - panic(err) - } + createOpts := servergroups.CreateOpts{ + Name: "my_sg", + Policy: "anti-affinity", + Rules: &servergroups.Rules{ + MaxServerPerHost: 3, + }, + } + + computeClient.Microversion = "2.64" + result := servergroups.Create(computeClient, createOpts) + + serverGroup, err := result.Extract() + if err != nil { + panic(err) + } Example to Delete a Server Group diff --git a/openstack/compute/v2/extensions/serverusage/doc.go b/openstack/compute/v2/extensions/serverusage/doc.go index 0f3127f042..f6310d77a9 100644 --- a/openstack/compute/v2/extensions/serverusage/doc.go +++ b/openstack/compute/v2/extensions/serverusage/doc.go @@ -4,17 +4,17 @@ with the extended usage information. Example to Get an extended information: - type serverUsageExt struct { - servers.Server - serverusage.UsageExt - } - var serverWithUsageExt serverUsageExt + type serverUsageExt struct { + servers.Server + serverusage.UsageExt + } + var serverWithUsageExt serverUsageExt - err := servers.Get(computeClient, "d650a0ce-17c3-497d-961a-43c4af80998a").ExtractInto(&serverWithUsageExt) - if err != nil { - panic(err) - } + err := servers.Get(computeClient, "d650a0ce-17c3-497d-961a-43c4af80998a").ExtractInto(&serverWithUsageExt) + if err != nil { + panic(err) + } - fmt.Printf("%+v\n", serverWithUsageExt) + fmt.Printf("%+v\n", serverWithUsageExt) */ package serverusage diff --git a/openstack/compute/v2/extensions/services/testing/fixtures.go b/openstack/compute/v2/extensions/services/testing/fixtures.go index 9a2813ea07..8811ba9b3b 100644 --- a/openstack/compute/v2/extensions/services/testing/fixtures.go +++ b/openstack/compute/v2/extensions/services/testing/fixtures.go @@ -238,7 +238,7 @@ const ServiceUpdate = ` } ` -//FakeServiceUpdateBody represents the updated service +// FakeServiceUpdateBody represents the updated service var FakeServiceUpdateBody = services.Service{ Binary: "nova-scheduler", DisabledReason: "test1", diff --git a/openstack/compute/v2/extensions/tags/doc.go b/openstack/compute/v2/extensions/tags/doc.go index f3ef25a15e..d3516aa0d8 100644 --- a/openstack/compute/v2/extensions/tags/doc.go +++ b/openstack/compute/v2/extensions/tags/doc.go @@ -5,66 +5,66 @@ This extension is available since 2.26 Compute V2 API microversion. Example to List all server Tags - client.Microversion = "2.26" + client.Microversion = "2.26" - serverTags, err := tags.List(client, serverID).Extract() - if err != nil { - log.Fatal(err) - } + serverTags, err := tags.List(client, serverID).Extract() + if err != nil { + log.Fatal(err) + } - fmt.Printf("Tags: %v\n", serverTags) + fmt.Printf("Tags: %v\n", serverTags) Example to Check if the specific Tag exists on a server - client.Microversion = "2.26" + client.Microversion = "2.26" - exists, err := tags.Check(client, serverID, tag).Extract() - if err != nil { - log.Fatal(err) - } + exists, err := tags.Check(client, serverID, tag).Extract() + if err != nil { + log.Fatal(err) + } - if exists { - log.Printf("Tag %s is set\n", tag) - } else { - log.Printf("Tag %s is not set\n", tag) - } + if exists { + log.Printf("Tag %s is set\n", tag) + } else { + log.Printf("Tag %s is not set\n", tag) + } Example to Replace all Tags on a server - client.Microversion = "2.26" + client.Microversion = "2.26" - newTags, err := tags.ReplaceAll(client, serverID, tags.ReplaceAllOpts{Tags: []string{"foo", "bar"}}).Extract() - if err != nil { - log.Fatal(err) - } + newTags, err := tags.ReplaceAll(client, serverID, tags.ReplaceAllOpts{Tags: []string{"foo", "bar"}}).Extract() + if err != nil { + log.Fatal(err) + } - fmt.Printf("New tags: %v\n", newTags) + fmt.Printf("New tags: %v\n", newTags) Example to Add a new Tag on a server - client.Microversion = "2.26" + client.Microversion = "2.26" - err := tags.Add(client, serverID, "foo").ExtractErr() - if err != nil { - log.Fatal(err) - } + err := tags.Add(client, serverID, "foo").ExtractErr() + if err != nil { + log.Fatal(err) + } Example to Delete a Tag on a server - client.Microversion = "2.26" + client.Microversion = "2.26" - err := tags.Delete(client, serverID, "foo").ExtractErr() - if err != nil { - log.Fatal(err) - } + err := tags.Delete(client, serverID, "foo").ExtractErr() + if err != nil { + log.Fatal(err) + } Example to Delete all Tags on a server - client.Microversion = "2.26" + client.Microversion = "2.26" - err := tags.DeleteAll(client, serverID).ExtractErr() - if err != nil { - log.Fatal(err) - } + err := tags.DeleteAll(client, serverID).ExtractErr() + if err != nil { + log.Fatal(err) + } */ package tags diff --git a/openstack/compute/v2/extensions/usage/doc.go b/openstack/compute/v2/extensions/usage/doc.go index 16b3a284ba..df9d79e0b9 100644 --- a/openstack/compute/v2/extensions/usage/doc.go +++ b/openstack/compute/v2/extensions/usage/doc.go @@ -54,6 +54,5 @@ Example to Retrieve Usage for All Tenants: if err != nil { panic(err) } - */ package usage diff --git a/openstack/compute/v2/extensions/volumeattach/testing/requests_test.go b/openstack/compute/v2/extensions/volumeattach/testing/requests_test.go index 60d80ca125..1b578eb0a6 100644 --- a/openstack/compute/v2/extensions/volumeattach/testing/requests_test.go +++ b/openstack/compute/v2/extensions/volumeattach/testing/requests_test.go @@ -32,7 +32,7 @@ var ExpectedVolumeAttachmentSlice = []volumeattach.VolumeAttachment{FirstVolumeA var iTag = "foo" var iTrue = true -//CreatedVolumeAttachment is the parsed result from CreatedOutput. +// CreatedVolumeAttachment is the parsed result from CreatedOutput. var CreatedVolumeAttachment = volumeattach.VolumeAttachment{ Device: "/dev/vdc", ID: "a26887c6-c47b-4654-abb5-dfadf7d3f804", diff --git a/openstack/compute/v2/flavors/requests.go b/openstack/compute/v2/flavors/requests.go index 1b7acd0a7e..2c527b79fe 100644 --- a/openstack/compute/v2/flavors/requests.go +++ b/openstack/compute/v2/flavors/requests.go @@ -12,12 +12,12 @@ type ListOptsBuilder interface { } /* - AccessType maps to OpenStack's Flavor.is_public field. Although the is_public - field is boolean, the request options are ternary, which is why AccessType is - a string. The following values are allowed: +AccessType maps to OpenStack's Flavor.is_public field. Although the is_public +field is boolean, the request options are ternary, which is why AccessType is +a string. The following values are allowed: - The AccessType arguement is optional, and if it is not supplied, OpenStack - returns the PublicAccess flavors. +The AccessType arguement is optional, and if it is not supplied, OpenStack +returns the PublicAccess flavors. */ type AccessType string @@ -35,12 +35,12 @@ const ( ) /* - ListOpts filters the results returned by the List() function. - For example, a flavor with a minDisk field of 10 will not be returned if you - specify MinDisk set to 20. +ListOpts filters the results returned by the List() function. +For example, a flavor with a minDisk field of 10 will not be returned if you +specify MinDisk set to 20. - Typically, software will use the last ID of the previous call to List to set - the Marker for the current call. +Typically, software will use the last ID of the previous call to List to set +the Marker for the current call. */ type ListOpts struct { // ChangesSince, if provided, instructs List to return only those things which diff --git a/openstack/compute/v2/servers/requests.go b/openstack/compute/v2/servers/requests.go index 72ec69e503..656e2de4d7 100644 --- a/openstack/compute/v2/servers/requests.go +++ b/openstack/compute/v2/servers/requests.go @@ -412,19 +412,19 @@ func (opts RebootOpts) ToServerRebootMap() (map[string]interface{}, error) { } /* - Reboot requests that a given server reboot. +Reboot requests that a given server reboot. - Two methods exist for rebooting a server: +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. +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. +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() diff --git a/openstack/compute/v2/servers/results.go b/openstack/compute/v2/servers/results.go index 8cfb8958e8..b92c666783 100644 --- a/openstack/compute/v2/servers/results.go +++ b/openstack/compute/v2/servers/results.go @@ -99,7 +99,8 @@ type GetPasswordResult struct { // If privateKey != nil the password is decrypted with the private key. // If privateKey == nil the encrypted password is returned and can be decrypted // with: -// echo '<pwd>' | base64 -D | openssl rsautl -decrypt -inkey <private_key> +// +// echo '<pwd>' | base64 -D | openssl rsautl -decrypt -inkey <private_key> func (r GetPasswordResult) ExtractPassword(privateKey *rsa.PrivateKey) (string, error) { var s struct { Password string `json:"password"` diff --git a/openstack/compute/v2/servers/testing/results_test.go b/openstack/compute/v2/servers/testing/results_test.go index 80c2cb2052..cfa40f665c 100644 --- a/openstack/compute/v2/servers/testing/results_test.go +++ b/openstack/compute/v2/servers/testing/results_test.go @@ -48,7 +48,8 @@ func TestExtractPassword_encrypted_pwd(t *testing.T) { // Ok - return decrypted password when private key is given. // Decrytion can be verified by: -// echo "<enc_pwd>" | base64 -D | openssl rsautl -decrypt -inkey <privateKey.pem> +// +// echo "<enc_pwd>" | base64 -D | openssl rsautl -decrypt -inkey <privateKey.pem> func TestExtractPassword_decrypted_pwd(t *testing.T) { privateKey, err := ssh.ParseRawPrivateKey([]byte(` diff --git a/openstack/containerinfra/v1/clusters/doc.go b/openstack/containerinfra/v1/clusters/doc.go index 334afd9db3..69203ff79c 100644 --- a/openstack/containerinfra/v1/clusters/doc.go +++ b/openstack/containerinfra/v1/clusters/doc.go @@ -57,19 +57,19 @@ Example to List Clusters Example to List Clusters with detailed information - allPagesDetail, err := clusters.ListDetail(serviceClient, clusters.ListOpts{}).AllPages() - if err != nil { - panic(err) - } + allPagesDetail, err := clusters.ListDetail(serviceClient, clusters.ListOpts{}).AllPages() + if err != nil { + panic(err) + } - allClustersDetail, err := clusters.ExtractClusters(allPagesDetail) - if err != nil { - panic(err) - } + allClustersDetail, err := clusters.ExtractClusters(allPagesDetail) + if err != nil { + panic(err) + } - for _, clusterDetail := range allClustersDetail { - fmt.Printf("%+v\n", clusterDetail) - } + for _, clusterDetail := range allClustersDetail { + fmt.Printf("%+v\n", clusterDetail) + } Example to Update a Cluster @@ -109,6 +109,5 @@ Example to Delete a Cluster if err != nil { panic(err) } - */ package clusters diff --git a/openstack/containerinfra/v1/nodegroups/doc.go b/openstack/containerinfra/v1/nodegroups/doc.go index c354de396c..ef1d55d9b4 100644 --- a/openstack/containerinfra/v1/nodegroups/doc.go +++ b/openstack/containerinfra/v1/nodegroups/doc.go @@ -4,115 +4,109 @@ Package nodegroups provides methods for interacting with the Magnum node group A All node group actions must be performed on a specific cluster, so the cluster UUID/name is required as a parameter in each method. - Create a client to use: - opts, err := openstack.AuthOptionsFromEnv() - if err != nil { - panic(err) - } - - provider, err := openstack.AuthenticatedClient(opts) - if err != nil { - panic(err) - } + opts, err := openstack.AuthOptionsFromEnv() + if err != nil { + panic(err) + } - client, err := openstack.NewContainerInfraV1(provider, gophercloud.EndpointOpts{Region: os.Getenv("OS_REGION_NAME")}) - if err != nil { - panic(err) - } + provider, err := openstack.AuthenticatedClient(opts) + if err != nil { + panic(err) + } - client.Microversion = "1.9" + client, err := openstack.NewContainerInfraV1(provider, gophercloud.EndpointOpts{Region: os.Getenv("OS_REGION_NAME")}) + if err != nil { + panic(err) + } + client.Microversion = "1.9" Example of Getting a node group: - ng, err := nodegroups.Get(client, clusterUUID, nodeGroupUUID).Extract() - if err != nil { - panic(err) - } - fmt.Printf("%#v\n", ng) - + ng, err := nodegroups.Get(client, clusterUUID, nodeGroupUUID).Extract() + if err != nil { + panic(err) + } + fmt.Printf("%#v\n", ng) Example of Listing node groups: - listOpts := nodegroup.ListOpts{ - Role: "worker", - } + listOpts := nodegroup.ListOpts{ + Role: "worker", + } - allPages, err := nodegroups.List(client, clusterUUID, listOpts).AllPages() - if err != nil { - panic(err) - } + allPages, err := nodegroups.List(client, clusterUUID, listOpts).AllPages() + if err != nil { + panic(err) + } - ngs, err := nodegroups.ExtractNodeGroups(allPages) - if err != nil { - panic(err) - } - - for _, ng := range ngs { - fmt.Printf("%#v\n", ng) - } + ngs, err := nodegroups.ExtractNodeGroups(allPages) + if err != nil { + panic(err) + } + for _, ng := range ngs { + fmt.Printf("%#v\n", ng) + } Example of Creating a node group: - // Labels, node image and node flavor will be inherited from the cluster value if not set. - // Role will default to "worker" if not set. - - // To add a label to the new node group, need to know the cluster labels - cluster, err := clusters.Get(client, clusterUUID).Extract() - if err != nil { - panic(err) - } + // Labels, node image and node flavor will be inherited from the cluster value if not set. + // Role will default to "worker" if not set. - // Add the new label - labels := cluster.Labels - labels["availability_zone"] = "A" + // To add a label to the new node group, need to know the cluster labels + cluster, err := clusters.Get(client, clusterUUID).Extract() + if err != nil { + panic(err) + } - maxNodes := 5 - createOpts := nodegroups.CreateOpts{ - Name: "new-nodegroup", - MinNodeCount: 2, - MaxNodeCount: &maxNodes, - Labels: labels, - } + // Add the new label + labels := cluster.Labels + labels["availability_zone"] = "A" - ng, err := nodegroups.Create(client, clusterUUID, createOpts).Extract() - if err != nil { - panic(err) - } + maxNodes := 5 + createOpts := nodegroups.CreateOpts{ + Name: "new-nodegroup", + MinNodeCount: 2, + MaxNodeCount: &maxNodes, + Labels: labels, + } - fmt.Printf("%#v\n", ng) + ng, err := nodegroups.Create(client, clusterUUID, createOpts).Extract() + if err != nil { + panic(err) + } + fmt.Printf("%#v\n", ng) Example of Updating a node group: - // Valid paths are "/min_node_count" and "/max_node_count". - // Max node count can be unset with the "remove" op to have - // no enforced maximum node count. - - updateOpts := []nodegroups.UpdateOptsBuilder{ - nodegroups.UpdateOpts{ - Op: nodegroups.ReplaceOp, - Path: "/max_node_count", - Value: 10, - }, - } + // Valid paths are "/min_node_count" and "/max_node_count". + // Max node count can be unset with the "remove" op to have + // no enforced maximum node count. - ng, err = nodegroups.Update(client, clusterUUID, nodeGroupUUID, updateOpts).Extract() - if err != nil { - panic(err) - } + updateOpts := []nodegroups.UpdateOptsBuilder{ + nodegroups.UpdateOpts{ + Op: nodegroups.ReplaceOp, + Path: "/max_node_count", + Value: 10, + }, + } - fmt.Printf("%#v\n", ng) + ng, err = nodegroups.Update(client, clusterUUID, nodeGroupUUID, updateOpts).Extract() + if err != nil { + panic(err) + } + fmt.Printf("%#v\n", ng) Example of Deleting a node group: - err = nodegroups.Delete(client, clusterUUID, nodeGroupUUID).ExtractErr() - if err != nil { - panic(err) - } + err = nodegroups.Delete(client, clusterUUID, nodeGroupUUID).ExtractErr() + if err != nil { + panic(err) + } */ package nodegroups diff --git a/openstack/containerinfra/v1/quotas/doc.go b/openstack/containerinfra/v1/quotas/doc.go index 62b13b2db0..ef6dfb666e 100644 --- a/openstack/containerinfra/v1/quotas/doc.go +++ b/openstack/containerinfra/v1/quotas/doc.go @@ -13,6 +13,5 @@ Example to Create a Quota if err != nil { panic(err) } - */ package quotas diff --git a/openstack/dns/v2/transfer/accept/doc.go b/openstack/dns/v2/transfer/accept/doc.go index 44d053875a..1bb8997578 100644 --- a/openstack/dns/v2/transfer/accept/doc.go +++ b/openstack/dns/v2/transfer/accept/doc.go @@ -4,40 +4,40 @@ resource for the OpenStack DNS service. Example to List Zone Transfer Accepts - // Optionaly you can provide Status as query parameter for filtering the result. - allPages, err := transferAccepts.List(dnsClient, nil).AllPages() - if err != nil { - panic(err) - } + // Optionaly you can provide Status as query parameter for filtering the result. + allPages, err := transferAccepts.List(dnsClient, nil).AllPages() + if err != nil { + panic(err) + } - allTransferAccepts, err := transferAccepts.ExtractTransferAccepts(allPages) - if err != nil { - panic(err) - } + allTransferAccepts, err := transferAccepts.ExtractTransferAccepts(allPages) + if err != nil { + panic(err) + } - for _, transferAccept := range allTransferAccepts { - fmt.Printf("%+v\n", transferAccept) - } + for _, transferAccept := range allTransferAccepts { + fmt.Printf("%+v\n", transferAccept) + } Example to Create a Zone Transfer Accept - zoneTransferRequestID := "99d10f68-5623-4491-91a0-6daafa32b60e" - key := "JKHGD2F7" - createOpts := transferAccepts.CreateOpts{ - ZoneTransferRequestID: zoneTransferRequestID, - Key: key, - } - transferAccept, err := transferAccepts.Create(dnsClient, createOpts).Extract() - if err != nil { - panic(err) - } + zoneTransferRequestID := "99d10f68-5623-4491-91a0-6daafa32b60e" + key := "JKHGD2F7" + createOpts := transferAccepts.CreateOpts{ + ZoneTransferRequestID: zoneTransferRequestID, + Key: key, + } + transferAccept, err := transferAccepts.Create(dnsClient, createOpts).Extract() + if err != nil { + panic(err) + } Example to Get a Zone Transfer Accept - transferAcceptID := "99d10f68-5623-4491-91a0-6daafa32b60e" - transferAccept, err := transferAccepts.Get(dnsClient, transferAcceptID).Extract() - if err != nil { - panic(err) - } + transferAcceptID := "99d10f68-5623-4491-91a0-6daafa32b60e" + transferAccept, err := transferAccepts.Get(dnsClient, transferAcceptID).Extract() + if err != nil { + panic(err) + } */ package accept diff --git a/openstack/dns/v2/transfer/request/doc.go b/openstack/dns/v2/transfer/request/doc.go index 05316687e5..6acb51adfa 100644 --- a/openstack/dns/v2/transfer/request/doc.go +++ b/openstack/dns/v2/transfer/request/doc.go @@ -4,39 +4,39 @@ resource for the OpenStack DNS service. Example to List Zone Transfer Requests - allPages, err := transferRequests.List(dnsClient, nil).AllPages() - if err != nil { - panic(err) - } + allPages, err := transferRequests.List(dnsClient, nil).AllPages() + if err != nil { + panic(err) + } - allTransferRequests, err := transferRequests.ExtractTransferRequests(allPages) - if err != nil { - panic(err) - } + allTransferRequests, err := transferRequests.ExtractTransferRequests(allPages) + if err != nil { + panic(err) + } - for _, transferRequest := range allTransferRequests { - fmt.Printf("%+v\n", transferRequest) - } + for _, transferRequest := range allTransferRequests { + fmt.Printf("%+v\n", transferRequest) + } Example to Create a Zone Transfer Request - zoneID := "99d10f68-5623-4491-91a0-6daafa32b60e" - targetProjectID := "f977bd7c-6485-4385-b04f-b5af0d186fcc" - createOpts := transferRequests.CreateOpts{ - TargetProjectID: targetProjectID, - Description: "This is a zone transfer request.", - } - transferRequest, err := transferRequests.Create(dnsClient, zoneID, createOpts).Extract() - if err != nil { - panic(err) - } + zoneID := "99d10f68-5623-4491-91a0-6daafa32b60e" + targetProjectID := "f977bd7c-6485-4385-b04f-b5af0d186fcc" + createOpts := transferRequests.CreateOpts{ + TargetProjectID: targetProjectID, + Description: "This is a zone transfer request.", + } + transferRequest, err := transferRequests.Create(dnsClient, zoneID, createOpts).Extract() + if err != nil { + panic(err) + } Example to Delete a Zone Transfer Request - transferID := "99d10f68-5623-4491-91a0-6daafa32b60e" - err := transferRequests.Delete(dnsClient, transferID).ExtractErr() - if err != nil { - panic(err) - } + transferID := "99d10f68-5623-4491-91a0-6daafa32b60e" + err := transferRequests.Delete(dnsClient, transferID).ExtractErr() + if err != nil { + panic(err) + } */ package request diff --git a/openstack/identity/v3/endpoints/doc.go b/openstack/identity/v3/endpoints/doc.go index 5822017c90..27e2378e06 100644 --- a/openstack/identity/v3/endpoints/doc.go +++ b/openstack/identity/v3/endpoints/doc.go @@ -44,7 +44,6 @@ Example to Create an Endpoint panic(err) } - Example to Update an Endpoint endpointID := "ad59deeec5154d1fa0dcff518596f499" diff --git a/openstack/identity/v3/extensions/ec2credentials/doc.go b/openstack/identity/v3/extensions/ec2credentials/doc.go index d20ff95b05..174cea5237 100644 --- a/openstack/identity/v3/extensions/ec2credentials/doc.go +++ b/openstack/identity/v3/extensions/ec2credentials/doc.go @@ -16,6 +16,5 @@ Example to Create an EC2 credential if err != nil { panic(err) } - */ package ec2credentials diff --git a/openstack/identity/v3/extensions/ec2tokens/doc.go b/openstack/identity/v3/extensions/ec2tokens/doc.go index 1f6f807fe0..a30d0faf3a 100644 --- a/openstack/identity/v3/extensions/ec2tokens/doc.go +++ b/openstack/identity/v3/extensions/ec2tokens/doc.go @@ -36,6 +36,5 @@ Example to auth a client using EC2 access and secret keys if err != nil { panic(err) } - */ package ec2tokens diff --git a/openstack/identity/v3/extensions/oauth1/doc.go b/openstack/identity/v3/extensions/oauth1/doc.go index c5b0831ca1..4294ef6c89 100644 --- a/openstack/identity/v3/extensions/oauth1/doc.go +++ b/openstack/identity/v3/extensions/oauth1/doc.go @@ -118,6 +118,5 @@ Example to Create a Token using OAuth1 method if err != nil { panic(err) } - */ package oauth1 diff --git a/openstack/identity/v3/extensions/projectendpoints/doc.go b/openstack/identity/v3/extensions/projectendpoints/doc.go index 10cdd2c136..ac07220ee2 100644 --- a/openstack/identity/v3/extensions/projectendpoints/doc.go +++ b/openstack/identity/v3/extensions/projectendpoints/doc.go @@ -22,6 +22,5 @@ Example to List Project Endpoints for _, endpoint := range allEndpoints { fmt.Printf("%+v\n", endpoint) } - */ package projectendpoints diff --git a/openstack/identity/v3/extensions/trusts/doc.go b/openstack/identity/v3/extensions/trusts/doc.go index b6118874a3..d5182cd3e6 100644 --- a/openstack/identity/v3/extensions/trusts/doc.go +++ b/openstack/identity/v3/extensions/trusts/doc.go @@ -25,43 +25,43 @@ Example to Create a Token with Username, Password, and Trust ID Example to Create a Trust - expiresAt := time.Date(2019, 12, 1, 14, 0, 0, 999999999, time.UTC) - createOpts := trusts.CreateOpts{ - ExpiresAt: &expiresAt, - Impersonation: true, - AllowRedelegation: true, - ProjectID: "9b71012f5a4a4aef9193f1995fe159b2", - Roles: []trusts.Role{ - { - Name: "member", - }, - }, - TrusteeUserID: "ecb37e88cc86431c99d0332208cb6fbf", - TrustorUserID: "959ed913a32c4ec88c041c98e61cbbc3", - } - - trust, err := trusts.Create(identityClient, createOpts).Extract() - if err != nil { - panic(err) - } - - fmt.Printf("Trust: %+v\n", trust) + expiresAt := time.Date(2019, 12, 1, 14, 0, 0, 999999999, time.UTC) + createOpts := trusts.CreateOpts{ + ExpiresAt: &expiresAt, + Impersonation: true, + AllowRedelegation: true, + ProjectID: "9b71012f5a4a4aef9193f1995fe159b2", + Roles: []trusts.Role{ + { + Name: "member", + }, + }, + TrusteeUserID: "ecb37e88cc86431c99d0332208cb6fbf", + TrustorUserID: "959ed913a32c4ec88c041c98e61cbbc3", + } + + trust, err := trusts.Create(identityClient, createOpts).Extract() + if err != nil { + panic(err) + } + + fmt.Printf("Trust: %+v\n", trust) Example to Delete a Trust - trustID := "3422b7c113894f5d90665e1a79655e23" - err := trusts.Delete(identityClient, trustID).ExtractErr() - if err != nil { - panic(err) - } + trustID := "3422b7c113894f5d90665e1a79655e23" + err := trusts.Delete(identityClient, trustID).ExtractErr() + if err != nil { + panic(err) + } Example to Get a Trust - trustID := "3422b7c113894f5d90665e1a79655e23" - err := trusts.Get(identityClient, trustID).ExtractErr() - if err != nil { - panic(err) - } + trustID := "3422b7c113894f5d90665e1a79655e23" + err := trusts.Get(identityClient, trustID).ExtractErr() + if err != nil { + panic(err) + } Example to List a Trust diff --git a/openstack/identity/v3/limits/doc.go b/openstack/identity/v3/limits/doc.go index 7c390c3bf5..db2fc8a2ed 100644 --- a/openstack/identity/v3/limits/doc.go +++ b/openstack/identity/v3/limits/doc.go @@ -17,6 +17,5 @@ Example to List Limits if err != nil { panic(err) } - */ package limits diff --git a/openstack/identity/v3/services/doc.go b/openstack/identity/v3/services/doc.go index 81702359ac..e0a79c2726 100644 --- a/openstack/identity/v3/services/doc.go +++ b/openstack/identity/v3/services/doc.go @@ -61,6 +61,5 @@ Example to Delete a Service if err != nil { panic(err) } - */ package services diff --git a/openstack/identity/v3/tokens/doc.go b/openstack/identity/v3/tokens/doc.go index 966e128f12..de74c82ecd 100644 --- a/openstack/identity/v3/tokens/doc.go +++ b/openstack/identity/v3/tokens/doc.go @@ -103,6 +103,5 @@ Example to Create a Token from a Username and Password with Project Name Scope if err != nil { panic(err) } - */ package tokens diff --git a/openstack/identity/v3/users/doc.go b/openstack/identity/v3/users/doc.go index 994ce71bcb..09133cda9f 100644 --- a/openstack/identity/v3/users/doc.go +++ b/openstack/identity/v3/users/doc.go @@ -167,6 +167,5 @@ Example to List Users in a Group for _, user := range allUsers { fmt.Printf("%+v\n", user) } - */ package users diff --git a/openstack/imageservice/v2/imagedata/doc.go b/openstack/imageservice/v2/imagedata/doc.go index a9d7a58948..20a5108396 100644 --- a/openstack/imageservice/v2/imagedata/doc.go +++ b/openstack/imageservice/v2/imagedata/doc.go @@ -18,18 +18,18 @@ Example to Upload Image Data Example to Stage Image Data - imageID := "da3b75d9-3f4a-40e7-8a2c-bfab23927dea" - - imageData, err := os.Open("/path/to/image/file") - if err != nil { - panic(err) - } - defer imageData.Close() - - err = imagedata.Stage(imageClient, imageID, imageData).ExtractErr() - if err != nil { - panic(err) - } + imageID := "da3b75d9-3f4a-40e7-8a2c-bfab23927dea" + + imageData, err := os.Open("/path/to/image/file") + if err != nil { + panic(err) + } + defer imageData.Close() + + err = imagedata.Stage(imageClient, imageID, imageData).ExtractErr() + if err != nil { + panic(err) + } Example to Download Image Data diff --git a/openstack/imageservice/v2/imageimport/doc.go b/openstack/imageservice/v2/imageimport/doc.go index 7772445651..775a3630b7 100644 --- a/openstack/imageservice/v2/imageimport/doc.go +++ b/openstack/imageservice/v2/imageimport/doc.go @@ -4,24 +4,24 @@ Imageservice Import API information. Example to Get an information about the Import API - importInfo, err := imageimport.Get(imagesClient).Extract() - if err != nil { - panic(err) - } + importInfo, err := imageimport.Get(imagesClient).Extract() + if err != nil { + panic(err) + } - fmt.Printf("%+v\n", importInfo) + fmt.Printf("%+v\n", importInfo) Example to Create a new image import - createOpts := imageimport.CreateOpts{ - Name: imageimport.WebDownloadMethod, - URI: "http://download.cirros-cloud.net/0.4.0/cirros-0.4.0-x86_64-disk.img", - } - imageID := "da3b75d9-3f4a-40e7-8a2c-bfab23927dea" + createOpts := imageimport.CreateOpts{ + Name: imageimport.WebDownloadMethod, + URI: "http://download.cirros-cloud.net/0.4.0/cirros-0.4.0-x86_64-disk.img", + } + imageID := "da3b75d9-3f4a-40e7-8a2c-bfab23927dea" - err := imageimport.Create(imagesClient, imageID, createOpts).ExtractErr() - if err != nil { - panic(err) - } + err := imageimport.Create(imagesClient, imageID, createOpts).ExtractErr() + if err != nil { + panic(err) + } */ package imageimport diff --git a/openstack/imageservice/v2/members/requests.go b/openstack/imageservice/v2/members/requests.go index e7dc42b15c..8d1c29e780 100644 --- a/openstack/imageservice/v2/members/requests.go +++ b/openstack/imageservice/v2/members/requests.go @@ -6,22 +6,22 @@ import ( ) /* - Create member for specific image +Create member for specific image - Preconditions +# Preconditions - * The specified images must exist. - * You can only add a new member to an image which 'visibility' attribute is - private. - * You must be the owner of the specified image. + - The specified images must exist. + - You can only add a new member to an image which 'visibility' attribute is + private. + - You must be the owner of the specified image. - Synchronous Postconditions +# Synchronous Postconditions - With correct permissions, you can see the member status of the image as - pending through API calls. +With correct permissions, you can see the member status of the image as +pending through API calls. - More details here: - http://developer.openstack.org/api-ref-image-v2.html#createImageMember-v2 +More details here: +http://developer.openstack.org/api-ref-image-v2.html#createImageMember-v2 */ func Create(client *gophercloud.ServiceClient, id string, member string) (r CreateResult) { b := map[string]interface{}{"member": member} diff --git a/openstack/imageservice/v2/tasks/doc.go b/openstack/imageservice/v2/tasks/doc.go index 28ed82e55c..7904c6a832 100644 --- a/openstack/imageservice/v2/tasks/doc.go +++ b/openstack/imageservice/v2/tasks/doc.go @@ -4,52 +4,52 @@ Imageservice. Example to List Tasks - listOpts := tasks.ListOpts{ - Owner: "424e7cf0243c468ca61732ba45973b3e", - } + listOpts := tasks.ListOpts{ + Owner: "424e7cf0243c468ca61732ba45973b3e", + } - allPages, err := tasks.List(imagesClient, listOpts).AllPages() - if err != nil { - panic(err) - } + allPages, err := tasks.List(imagesClient, listOpts).AllPages() + if err != nil { + panic(err) + } - allTasks, err := tasks.ExtractTasks(allPages) - if err != nil { - panic(err) - } + allTasks, err := tasks.ExtractTasks(allPages) + if err != nil { + panic(err) + } - for _, task := range allTasks { - fmt.Printf("%+v\n", task) - } + for _, task := range allTasks { + fmt.Printf("%+v\n", task) + } Example to Get a Task - task, err := tasks.Get(imagesClient, "1252f636-1246-4319-bfba-c47cde0efbe0").Extract() - if err != nil { - panic(err) - } + task, err := tasks.Get(imagesClient, "1252f636-1246-4319-bfba-c47cde0efbe0").Extract() + if err != nil { + panic(err) + } - fmt.Printf("%+v\n", task) + fmt.Printf("%+v\n", task) Example to Create a Task - createOpts := tasks.CreateOpts{ - Type: "import", - Input: map[string]interface{}{ - "image_properties": map[string]interface{}{ - "container_format": "bare", - "disk_format": "raw", - }, - "import_from_format": "raw", - "import_from": "https://cloud-images.ubuntu.com/bionic/current/bionic-server-cloudimg-amd64.img", - }, - } - - task, err := tasks.Create(imagesClient, createOpts).Extract() - if err != nil { - panic(err) - } - - fmt.Printf("%+v\n", task) + createOpts := tasks.CreateOpts{ + Type: "import", + Input: map[string]interface{}{ + "image_properties": map[string]interface{}{ + "container_format": "bare", + "disk_format": "raw", + }, + "import_from_format": "raw", + "import_from": "https://cloud-images.ubuntu.com/bionic/current/bionic-server-cloudimg-amd64.img", + }, + } + + task, err := tasks.Create(imagesClient, createOpts).Extract() + if err != nil { + panic(err) + } + + fmt.Printf("%+v\n", task) */ package tasks diff --git a/openstack/loadbalancer/v2/monitors/requests.go b/openstack/loadbalancer/v2/monitors/requests.go index bef74197d3..7d7466a081 100644 --- a/openstack/loadbalancer/v2/monitors/requests.go +++ b/openstack/loadbalancer/v2/monitors/requests.go @@ -149,19 +149,19 @@ func (opts CreateOpts) ToMonitorCreateMap() (map[string]interface{}, error) { } /* - Create is an operation which provisions a new Health Monitor. There are - different types of Monitor you can provision: PING, TCP or HTTP(S). Below - are examples of how to create each one. +Create is an operation which provisions a new Health Monitor. There are +different types of Monitor you can provision: PING, TCP or HTTP(S). Below +are examples of how to create each one. - Here is an example config struct to use when creating a PING or TCP Monitor: +Here is an example config struct to use when creating a PING or TCP Monitor: - CreateOpts{Type: TypePING, Delay: 20, Timeout: 10, MaxRetries: 3} - CreateOpts{Type: TypeTCP, Delay: 20, Timeout: 10, MaxRetries: 3} +CreateOpts{Type: TypePING, Delay: 20, Timeout: 10, MaxRetries: 3} +CreateOpts{Type: TypeTCP, Delay: 20, Timeout: 10, MaxRetries: 3} - Here is an example config struct to use when creating a HTTP(S) Monitor: +Here is an example config struct to use when creating a HTTP(S) Monitor: - CreateOpts{Type: TypeHTTP, Delay: 20, Timeout: 10, MaxRetries: 3, - HttpMethod: "HEAD", ExpectedCodes: "200", PoolID: "2c946bfc-1804-43ab-a2ff-58f6a762b505"} +CreateOpts{Type: TypeHTTP, Delay: 20, Timeout: 10, MaxRetries: 3, +HttpMethod: "HEAD", ExpectedCodes: "200", PoolID: "2c946bfc-1804-43ab-a2ff-58f6a762b505"} */ func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToMonitorCreateMap() diff --git a/openstack/loadbalancer/v2/pools/results.go b/openstack/loadbalancer/v2/pools/results.go index f5b8829fd1..b1466879b3 100644 --- a/openstack/loadbalancer/v2/pools/results.go +++ b/openstack/loadbalancer/v2/pools/results.go @@ -15,15 +15,20 @@ import ( // types of persistence are supported: // // SOURCE_IP: With this mode, all connections originating from the same source -// IP address, will be handled by the same Member of the Pool. +// +// IP address, will be handled by the same Member of the Pool. +// // HTTP_COOKIE: With this persistence mode, the load balancing function will -// create a cookie on the first request from a client. Subsequent -// requests containing the same cookie value will be handled by -// the same Member of the Pool. +// +// create a cookie on the first request from a client. Subsequent +// requests containing the same cookie value will be handled by +// the same Member of the Pool. +// // APP_COOKIE: With this persistence mode, the load balancing function will -// rely on a cookie established by the backend application. All -// requests carrying the same cookie value will be handled by the -// same Member of the Pool. +// +// rely on a cookie established by the backend application. All +// requests carrying the same cookie value will be handled by the +// same Member of the Pool. type SessionPersistence struct { // The type of persistence mode. Type string `json:"type"` diff --git a/openstack/loadbalancer/v2/providers/doc.go b/openstack/loadbalancer/v2/providers/doc.go index 695294b8a6..d03ea79237 100644 --- a/openstack/loadbalancer/v2/providers/doc.go +++ b/openstack/loadbalancer/v2/providers/doc.go @@ -17,6 +17,5 @@ Example to List Providers for _, p := range allProviders { fmt.Printf("%+v\n", p) } - */ package providers diff --git a/openstack/loadbalancer/v2/quotas/doc.go b/openstack/loadbalancer/v2/quotas/doc.go index 39d54b98f9..1dc27c67b2 100644 --- a/openstack/loadbalancer/v2/quotas/doc.go +++ b/openstack/loadbalancer/v2/quotas/doc.go @@ -3,32 +3,32 @@ Package quotas provides the ability to retrieve and manage Load Balancer quotas Example to Get project quotas - projectID = "23d5d3f79dfa4f73b72b8b0b0063ec55" - quotasInfo, err := quotas.Get(networkClient, projectID).Extract() - if err != nil { - log.Fatal(err) - } + projectID = "23d5d3f79dfa4f73b72b8b0b0063ec55" + quotasInfo, err := quotas.Get(networkClient, projectID).Extract() + if err != nil { + log.Fatal(err) + } - fmt.Printf("quotas: %#v\n", quotasInfo) + fmt.Printf("quotas: %#v\n", quotasInfo) Example to Update project quotas - projectID = "23d5d3f79dfa4f73b72b8b0b0063ec55" + projectID = "23d5d3f79dfa4f73b72b8b0b0063ec55" - updateOpts := quotas.UpdateOpts{ - Loadbalancer: gophercloud.IntToPointer(20), - Listener: gophercloud.IntToPointer(40), - Member: gophercloud.IntToPointer(200), - Pool: gophercloud.IntToPointer(20), - Healthmonitor: gophercloud.IntToPointer(1), - L7Policy: gophercloud.IntToPointer(50), - L7Rule: gophercloud.IntToPointer(100), - } - quotasInfo, err := quotas.Update(networkClient, projectID) - if err != nil { - log.Fatal(err) - } + updateOpts := quotas.UpdateOpts{ + Loadbalancer: gophercloud.IntToPointer(20), + Listener: gophercloud.IntToPointer(40), + Member: gophercloud.IntToPointer(200), + Pool: gophercloud.IntToPointer(20), + Healthmonitor: gophercloud.IntToPointer(1), + L7Policy: gophercloud.IntToPointer(50), + L7Rule: gophercloud.IntToPointer(100), + } + quotasInfo, err := quotas.Update(networkClient, projectID) + if err != nil { + log.Fatal(err) + } - fmt.Printf("quotas: %#v\n", quotasInfo) + fmt.Printf("quotas: %#v\n", quotasInfo) */ package quotas diff --git a/openstack/messaging/v2/queues/doc.go b/openstack/messaging/v2/queues/doc.go index ca97c52a8a..33092d6be0 100644 --- a/openstack/messaging/v2/queues/doc.go +++ b/openstack/messaging/v2/queues/doc.go @@ -6,24 +6,24 @@ Lists all queues and creates, shows information for updates, deletes, and action Example to List Queues - listOpts := queues.ListOpts{ - Limit: 10, - } + listOpts := queues.ListOpts{ + Limit: 10, + } - pager := queues.List(client, listOpts) + pager := queues.List(client, listOpts) - err = pager.EachPage(func(page pagination.Page) (bool, error) { - queues, err := queues.ExtractQueues(page) - if err != nil { - panic(err) - } + err = pager.EachPage(func(page pagination.Page) (bool, error) { + queues, err := queues.ExtractQueues(page) + if err != nil { + panic(err) + } - for _, queue := range queues { - fmt.Printf("%+v\n", queue) - } + for _, queue := range queues { + fmt.Printf("%+v\n", queue) + } - return true, nil - }) + return true, nil + }) Example to Create a Queue diff --git a/openstack/networking/v2/extensions/attributestags/doc.go b/openstack/networking/v2/extensions/attributestags/doc.go index 3257dd1bad..f34b1e2e30 100644 --- a/openstack/networking/v2/extensions/attributestags/doc.go +++ b/openstack/networking/v2/extensions/attributestags/doc.go @@ -7,12 +7,12 @@ See https://developer.openstack.org/api-ref/network/v2/#standard-attributes-tag- Example to ReplaceAll Resource Tags - network, err := networks.Create(conn, createOpts).Extract() + network, err := networks.Create(conn, createOpts).Extract() - tagReplaceAllOpts := attributestags.ReplaceAllOpts{ - Tags: []string{"abc", "123"}, - } - attributestags.ReplaceAll(conn, "networks", network.ID, tagReplaceAllOpts) + tagReplaceAllOpts := attributestags.ReplaceAllOpts{ + Tags: []string{"abc", "123"}, + } + attributestags.ReplaceAll(conn, "networks", network.ID, tagReplaceAllOpts) Example to List all Resource Tags @@ -24,11 +24,11 @@ Example to Delete all Resource Tags Example to Add a tag to a Resource - err = attributestags.Add(client, "networks", network.ID, "atag").ExtractErr() + err = attributestags.Add(client, "networks", network.ID, "atag").ExtractErr() Example to Delete a tag from a Resource - err = attributestags.Delete(client, "networks", network.ID, "atag").ExtractErr() + err = attributestags.Delete(client, "networks", network.ID, "atag").ExtractErr() Example to confirm if a tag exists on a resource diff --git a/openstack/networking/v2/extensions/layer3/addressscopes/doc.go b/openstack/networking/v2/extensions/layer3/addressscopes/doc.go index d68d2b764a..e25ee97fd8 100644 --- a/openstack/networking/v2/extensions/layer3/addressscopes/doc.go +++ b/openstack/networking/v2/extensions/layer3/addressscopes/doc.go @@ -3,62 +3,62 @@ Package addressscopes provides the ability to retrieve and manage Address scopes Example of Listing Address scopes - listOpts := addressscopes.ListOpts{ - IPVersion: 6, - } + listOpts := addressscopes.ListOpts{ + IPVersion: 6, + } - allPages, err := addressscopes.List(networkClient, listOpts).AllPages() - if err != nil { - panic(err) - } + allPages, err := addressscopes.List(networkClient, listOpts).AllPages() + if err != nil { + panic(err) + } - allAddressScopes, err := addressscopes.ExtractAddressScopes(allPages) - if err != nil { - panic(err) - } + allAddressScopes, err := addressscopes.ExtractAddressScopes(allPages) + if err != nil { + panic(err) + } - for _, addressScope := range allAddressScopes { - fmt.Printf("%+v\n", addressScope) - } + for _, addressScope := range allAddressScopes { + fmt.Printf("%+v\n", addressScope) + } Example to Get an Address scope - addressScopeID = "9cc35860-522a-4d35-974d-51d4b011801e" - addressScope, err := addressscopes.Get(networkClient, addressScopeID).Extract() - if err != nil { - panic(err) - } + addressScopeID = "9cc35860-522a-4d35-974d-51d4b011801e" + addressScope, err := addressscopes.Get(networkClient, addressScopeID).Extract() + if err != nil { + panic(err) + } Example to Create a new Address scope - addressScopeOpts := addressscopes.CreateOpts{ - Name: "my_address_scope", - IPVersion: 6, - } - addressScope, err := addressscopes.Create(networkClient, addressScopeOpts).Extract() - if err != nil { - panic(err) - } + addressScopeOpts := addressscopes.CreateOpts{ + Name: "my_address_scope", + IPVersion: 6, + } + addressScope, err := addressscopes.Create(networkClient, addressScopeOpts).Extract() + if err != nil { + panic(err) + } Example to Update an Address scope - addressScopeID = "9cc35860-522a-4d35-974d-51d4b011801e" - newName := "awesome_name" - updateOpts := addressscopes.UpdateOpts{ - Name: &newName, - } + addressScopeID = "9cc35860-522a-4d35-974d-51d4b011801e" + newName := "awesome_name" + updateOpts := addressscopes.UpdateOpts{ + Name: &newName, + } - addressScope, err := addressscopes.Update(networkClient, addressScopeID, updateOpts).Extract() - if err != nil { - panic(err) - } + addressScope, err := addressscopes.Update(networkClient, addressScopeID, updateOpts).Extract() + if err != nil { + panic(err) + } Example to Delete an Address scope - addressScopeID = "9cc35860-522a-4d35-974d-51d4b011801e" - err := addressscopes.Delete(networkClient, addressScopeID).ExtractErr() - if err != nil { - panic(err) - } + addressScopeID = "9cc35860-522a-4d35-974d-51d4b011801e" + err := addressscopes.Delete(networkClient, addressScopeID).ExtractErr() + if err != nil { + panic(err) + } */ package addressscopes diff --git a/openstack/networking/v2/extensions/layer3/portforwarding/doc.go b/openstack/networking/v2/extensions/layer3/portforwarding/doc.go index 661a06531e..c8f2744d8d 100644 --- a/openstack/networking/v2/extensions/layer3/portforwarding/doc.go +++ b/openstack/networking/v2/extensions/layer3/portforwarding/doc.go @@ -28,7 +28,6 @@ Example to Get a Port Forwarding with a certain ID panic(err) } - Example to Create a Port Forwarding for a floating IP createOpts := &portforwarding.CreateOpts{ diff --git a/openstack/networking/v2/extensions/lbaas/monitors/requests.go b/openstack/networking/v2/extensions/lbaas/monitors/requests.go index 3d3ff97bcf..1cc92e60b8 100644 --- a/openstack/networking/v2/extensions/lbaas/monitors/requests.go +++ b/openstack/networking/v2/extensions/lbaas/monitors/requests.go @@ -138,8 +138,8 @@ func (opts CreateOpts) ToLBMonitorCreateMap() (map[string]interface{}, error) { // Here is an example config struct to use when creating a HTTP(S) monitor: // // CreateOpts{Type: TypeHTTP, Delay: 20, Timeout: 10, MaxRetries: 3, -// HttpMethod: "HEAD", ExpectedCodes: "200"} // +// HttpMethod: "HEAD", ExpectedCodes: "200"} func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToLBMonitorCreateMap() if err != nil { diff --git a/openstack/networking/v2/extensions/lbaas/vips/results.go b/openstack/networking/v2/extensions/lbaas/vips/results.go index c99ee1ef6e..1351e352bb 100644 --- a/openstack/networking/v2/extensions/lbaas/vips/results.go +++ b/openstack/networking/v2/extensions/lbaas/vips/results.go @@ -11,15 +11,20 @@ import ( // types of persistence are supported: // // SOURCE_IP: With this mode, all connections originating from the same source -// IP address, will be handled by the same member of the pool. +// +// IP address, will be handled by the same member of the pool. +// // HTTP_COOKIE: With this persistence mode, the load balancing function will -// create a cookie on the first request from a client. Subsequent -// requests containing the same cookie value will be handled by -// the same member of the pool. +// +// create a cookie on the first request from a client. Subsequent +// requests containing the same cookie value will be handled by +// the same member of the pool. +// // APP_COOKIE: With this persistence mode, the load balancing function will -// rely on a cookie established by the backend application. All -// requests carrying the same cookie value will be handled by the -// same member of the pool. +// +// rely on a cookie established by the backend application. All +// requests carrying the same cookie value will be handled by the +// same member of the pool. type SessionPersistence struct { // Type is the type of persistence mode. Type string `json:"type"` diff --git a/openstack/networking/v2/extensions/lbaas_v2/monitors/requests.go b/openstack/networking/v2/extensions/lbaas_v2/monitors/requests.go index fa0afc3bf6..5fc513b184 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/monitors/requests.go +++ b/openstack/networking/v2/extensions/lbaas_v2/monitors/requests.go @@ -159,19 +159,19 @@ func (opts CreateOpts) ToMonitorCreateMap() (map[string]interface{}, error) { } /* - Create is an operation which provisions a new Health Monitor. There are - different types of Monitor you can provision: PING, TCP or HTTP(S). Below - are examples of how to create each one. +Create is an operation which provisions a new Health Monitor. There are +different types of Monitor you can provision: PING, TCP or HTTP(S). Below +are examples of how to create each one. - Here is an example config struct to use when creating a PING or TCP Monitor: +Here is an example config struct to use when creating a PING or TCP Monitor: - CreateOpts{Type: TypePING, Delay: 20, Timeout: 10, MaxRetries: 3} - CreateOpts{Type: TypeTCP, Delay: 20, Timeout: 10, MaxRetries: 3} +CreateOpts{Type: TypePING, Delay: 20, Timeout: 10, MaxRetries: 3} +CreateOpts{Type: TypeTCP, Delay: 20, Timeout: 10, MaxRetries: 3} - Here is an example config struct to use when creating a HTTP(S) Monitor: +Here is an example config struct to use when creating a HTTP(S) Monitor: - CreateOpts{Type: TypeHTTP, Delay: 20, Timeout: 10, MaxRetries: 3, - HttpMethod: "HEAD", ExpectedCodes: "200", PoolID: "2c946bfc-1804-43ab-a2ff-58f6a762b505"} +CreateOpts{Type: TypeHTTP, Delay: 20, Timeout: 10, MaxRetries: 3, +HttpMethod: "HEAD", ExpectedCodes: "200", PoolID: "2c946bfc-1804-43ab-a2ff-58f6a762b505"} */ func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToMonitorCreateMap() diff --git a/openstack/networking/v2/extensions/lbaas_v2/pools/results.go b/openstack/networking/v2/extensions/lbaas_v2/pools/results.go index fba0d3a878..761264e48e 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/pools/results.go +++ b/openstack/networking/v2/extensions/lbaas_v2/pools/results.go @@ -12,15 +12,20 @@ import ( // types of persistence are supported: // // SOURCE_IP: With this mode, all connections originating from the same source -// IP address, will be handled by the same Member of the Pool. +// +// IP address, will be handled by the same Member of the Pool. +// // HTTP_COOKIE: With this persistence mode, the load balancing function will -// create a cookie on the first request from a client. Subsequent -// requests containing the same cookie value will be handled by -// the same Member of the Pool. +// +// create a cookie on the first request from a client. Subsequent +// requests containing the same cookie value will be handled by +// the same Member of the Pool. +// // APP_COOKIE: With this persistence mode, the load balancing function will -// rely on a cookie established by the backend application. All -// requests carrying the same cookie value will be handled by the -// same Member of the Pool. +// +// rely on a cookie established by the backend application. All +// requests carrying the same cookie value will be handled by the +// same Member of the Pool. type SessionPersistence struct { // The type of persistence mode. Type string `json:"type"` diff --git a/openstack/networking/v2/extensions/networkipavailabilities/doc.go b/openstack/networking/v2/extensions/networkipavailabilities/doc.go index faadaa2227..f07cc7a8c7 100644 --- a/openstack/networking/v2/extensions/networkipavailabilities/doc.go +++ b/openstack/networking/v2/extensions/networkipavailabilities/doc.go @@ -4,27 +4,27 @@ networkipavailabilities through the Neutron API. Example of Listing NetworkIPAvailabilities - allPages, err := networkipavailabilities.List(networkClient, networkipavailabilities.ListOpts{}).AllPages() - if err != nil { - panic(err) - } + allPages, err := networkipavailabilities.List(networkClient, networkipavailabilities.ListOpts{}).AllPages() + if err != nil { + panic(err) + } - allAvailabilities, err := networkipavailabilities.ExtractNetworkIPAvailabilities(allPages) - if err != nil { - panic(err) - } + allAvailabilities, err := networkipavailabilities.ExtractNetworkIPAvailabilities(allPages) + if err != nil { + panic(err) + } - for _, availability := range allAvailabilities { - fmt.Printf("%+v\n", availability) - } + for _, availability := range allAvailabilities { + fmt.Printf("%+v\n", availability) + } Example of Getting a single NetworkIPAvailability - availability, err := networkipavailabilities.Get(networkClient, "cf11ab78-2302-49fa-870f-851a08c7afb8").Extract() - if err != nil { - panic(err) - } + availability, err := networkipavailabilities.Get(networkClient, "cf11ab78-2302-49fa-870f-851a08c7afb8").Extract() + if err != nil { + panic(err) + } - fmt.Printf("%+v\n", availability) + fmt.Printf("%+v\n", availability) */ package networkipavailabilities diff --git a/openstack/networking/v2/extensions/qos/policies/doc.go b/openstack/networking/v2/extensions/qos/policies/doc.go index 3386812ed0..f2346168b8 100644 --- a/openstack/networking/v2/extensions/qos/policies/doc.go +++ b/openstack/networking/v2/extensions/qos/policies/doc.go @@ -4,254 +4,254 @@ for the OpenStack Networking service. Example to Get a Port with a QoS policy - var portWithQoS struct { - ports.Port - policies.QoSPolicyExt - } + var portWithQoS struct { + ports.Port + policies.QoSPolicyExt + } - portID := "46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2" + portID := "46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2" - err = ports.Get(client, portID).ExtractInto(&portWithQoS) - if err != nil { - log.Fatal(err) - } + err = ports.Get(client, portID).ExtractInto(&portWithQoS) + if err != nil { + log.Fatal(err) + } - fmt.Printf("Port: %+v\n", portWithQoS) + fmt.Printf("Port: %+v\n", portWithQoS) Example to Create a Port with a QoS policy - var portWithQoS struct { - ports.Port - policies.QoSPolicyExt - } + var portWithQoS struct { + ports.Port + policies.QoSPolicyExt + } - policyID := "d6ae28ce-fcb5-4180-aa62-d260a27e09ae" - networkID := "7069db8d-e817-4b39-a654-d2dd76e73d36" + policyID := "d6ae28ce-fcb5-4180-aa62-d260a27e09ae" + networkID := "7069db8d-e817-4b39-a654-d2dd76e73d36" - portCreateOpts := ports.CreateOpts{ - NetworkID: networkID, - } + portCreateOpts := ports.CreateOpts{ + NetworkID: networkID, + } - createOpts := policies.PortCreateOptsExt{ - CreateOptsBuilder: portCreateOpts, - QoSPolicyID: policyID, - } + createOpts := policies.PortCreateOptsExt{ + CreateOptsBuilder: portCreateOpts, + QoSPolicyID: policyID, + } - err = ports.Create(client, createOpts).ExtractInto(&portWithQoS) - if err != nil { - panic(err) - } + err = ports.Create(client, createOpts).ExtractInto(&portWithQoS) + if err != nil { + panic(err) + } - fmt.Printf("Port: %+v\n", portWithQoS) + fmt.Printf("Port: %+v\n", portWithQoS) Example to Add a QoS policy to an existing Port - var portWithQoS struct { - ports.Port - policies.QoSPolicyExt - } + var portWithQoS struct { + ports.Port + policies.QoSPolicyExt + } - portUpdateOpts := ports.UpdateOpts{} + portUpdateOpts := ports.UpdateOpts{} - policyID := "d6ae28ce-fcb5-4180-aa62-d260a27e09ae" + policyID := "d6ae28ce-fcb5-4180-aa62-d260a27e09ae" - updateOpts := policies.PortUpdateOptsExt{ - UpdateOptsBuilder: portUpdateOpts, - QoSPolicyID: &policyID, - } + updateOpts := policies.PortUpdateOptsExt{ + UpdateOptsBuilder: portUpdateOpts, + QoSPolicyID: &policyID, + } - err := ports.Update(client, "65c0ee9f-d634-4522-8954-51021b570b0d", updateOpts).ExtractInto(&portWithQoS) - if err != nil { - panic(err) - } + err := ports.Update(client, "65c0ee9f-d634-4522-8954-51021b570b0d", updateOpts).ExtractInto(&portWithQoS) + if err != nil { + panic(err) + } - fmt.Printf("Port: %+v\n", portWithQoS) + fmt.Printf("Port: %+v\n", portWithQoS) Example to Delete a QoS policy from the existing Port - var portWithQoS struct { - ports.Port - policies.QoSPolicyExt - } + var portWithQoS struct { + ports.Port + policies.QoSPolicyExt + } - portUpdateOpts := ports.UpdateOpts{} + portUpdateOpts := ports.UpdateOpts{} - policyID := "" + policyID := "" - updateOpts := policies.PortUpdateOptsExt{ - UpdateOptsBuilder: portUpdateOpts, - QoSPolicyID: &policyID, - } + updateOpts := policies.PortUpdateOptsExt{ + UpdateOptsBuilder: portUpdateOpts, + QoSPolicyID: &policyID, + } - err := ports.Update(client, "65c0ee9f-d634-4522-8954-51021b570b0d", updateOpts).ExtractInto(&portWithQoS) - if err != nil { - panic(err) - } + err := ports.Update(client, "65c0ee9f-d634-4522-8954-51021b570b0d", updateOpts).ExtractInto(&portWithQoS) + if err != nil { + panic(err) + } - fmt.Printf("Port: %+v\n", portWithQoS) + fmt.Printf("Port: %+v\n", portWithQoS) Example to Get a Network with a QoS policy - var networkWithQoS struct { - networks.Network - policies.QoSPolicyExt - } + var networkWithQoS struct { + networks.Network + policies.QoSPolicyExt + } - networkID := "46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2" + networkID := "46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2" - err = networks.Get(client, networkID).ExtractInto(&networkWithQoS) - if err != nil { - log.Fatal(err) - } + err = networks.Get(client, networkID).ExtractInto(&networkWithQoS) + if err != nil { + log.Fatal(err) + } - fmt.Printf("Network: %+v\n", networkWithQoS) + fmt.Printf("Network: %+v\n", networkWithQoS) Example to Create a Network with a QoS policy - var networkWithQoS struct { - networks.Network - policies.QoSPolicyExt - } + var networkWithQoS struct { + networks.Network + policies.QoSPolicyExt + } - policyID := "d6ae28ce-fcb5-4180-aa62-d260a27e09ae" - networkID := "7069db8d-e817-4b39-a654-d2dd76e73d36" + policyID := "d6ae28ce-fcb5-4180-aa62-d260a27e09ae" + networkID := "7069db8d-e817-4b39-a654-d2dd76e73d36" - networkCreateOpts := networks.CreateOpts{ - NetworkID: networkID, - } + networkCreateOpts := networks.CreateOpts{ + NetworkID: networkID, + } - createOpts := policies.NetworkCreateOptsExt{ - CreateOptsBuilder: networkCreateOpts, - QoSPolicyID: policyID, - } + createOpts := policies.NetworkCreateOptsExt{ + CreateOptsBuilder: networkCreateOpts, + QoSPolicyID: policyID, + } - err = networks.Create(client, createOpts).ExtractInto(&networkWithQoS) - if err != nil { - panic(err) - } + err = networks.Create(client, createOpts).ExtractInto(&networkWithQoS) + if err != nil { + panic(err) + } - fmt.Printf("Network: %+v\n", networkWithQoS) + fmt.Printf("Network: %+v\n", networkWithQoS) Example to add a QoS policy to an existing Network - var networkWithQoS struct { - networks.Network - policies.QoSPolicyExt - } + var networkWithQoS struct { + networks.Network + policies.QoSPolicyExt + } - networkUpdateOpts := networks.UpdateOpts{} + networkUpdateOpts := networks.UpdateOpts{} - policyID := "d6ae28ce-fcb5-4180-aa62-d260a27e09ae" + policyID := "d6ae28ce-fcb5-4180-aa62-d260a27e09ae" - updateOpts := policies.NetworkUpdateOptsExt{ - UpdateOptsBuilder: networkUpdateOpts, - QoSPolicyID: &policyID, - } + updateOpts := policies.NetworkUpdateOptsExt{ + UpdateOptsBuilder: networkUpdateOpts, + QoSPolicyID: &policyID, + } - err := networks.Update(client, "65c0ee9f-d634-4522-8954-51021b570b0d", updateOpts).ExtractInto(&networkWithQoS) - if err != nil { - panic(err) - } + err := networks.Update(client, "65c0ee9f-d634-4522-8954-51021b570b0d", updateOpts).ExtractInto(&networkWithQoS) + if err != nil { + panic(err) + } - fmt.Printf("Network: %+v\n", networkWithQoS) + fmt.Printf("Network: %+v\n", networkWithQoS) Example to delete a QoS policy from the existing Network - var networkWithQoS struct { - networks.Network - policies.QoSPolicyExt - } + var networkWithQoS struct { + networks.Network + policies.QoSPolicyExt + } - networkUpdateOpts := networks.UpdateOpts{} + networkUpdateOpts := networks.UpdateOpts{} - policyID := "" + policyID := "" - updateOpts := policies.NetworkUpdateOptsExt{ - UpdateOptsBuilder: networkUpdateOpts, - QoSPolicyID: &policyID, - } + updateOpts := policies.NetworkUpdateOptsExt{ + UpdateOptsBuilder: networkUpdateOpts, + QoSPolicyID: &policyID, + } - err := networks.Update(client, "65c0ee9f-d634-4522-8954-51021b570b0d", updateOpts).ExtractInto(&networkWithQoS) - if err != nil { - panic(err) - } + err := networks.Update(client, "65c0ee9f-d634-4522-8954-51021b570b0d", updateOpts).ExtractInto(&networkWithQoS) + if err != nil { + panic(err) + } - fmt.Printf("Network: %+v\n", networkWithQoS) + fmt.Printf("Network: %+v\n", networkWithQoS) Example to List QoS policies - shared := true - listOpts := policies.ListOpts{ - Name: "shared-policy", - Shared: &shared, - } + shared := true + listOpts := policies.ListOpts{ + Name: "shared-policy", + Shared: &shared, + } - allPages, err := policies.List(networkClient, listOpts).AllPages() - if err != nil { - panic(err) - } + allPages, err := policies.List(networkClient, listOpts).AllPages() + if err != nil { + panic(err) + } - allPolicies, err := policies.ExtractPolicies(allPages) - if err != nil { - panic(err) - } + allPolicies, err := policies.ExtractPolicies(allPages) + if err != nil { + panic(err) + } - for _, policy := range allPolicies { - fmt.Printf("%+v\n", policy) - } + for _, policy := range allPolicies { + fmt.Printf("%+v\n", policy) + } Example to Get a specific QoS policy - policyID := "30a57f4a-336b-4382-8275-d708babd2241" + policyID := "30a57f4a-336b-4382-8275-d708babd2241" - policy, err := policies.Get(networkClient, policyID).Extract() - if err != nil { - panic(err) - } + policy, err := policies.Get(networkClient, policyID).Extract() + if err != nil { + panic(err) + } - fmt.Printf("%+v\n", policy) + fmt.Printf("%+v\n", policy) Example to Create a QoS policy - createOpts := policies.CreateOpts{ - Name: "shared-default-policy", - Shared: true, - IsDefault: true, - } + createOpts := policies.CreateOpts{ + Name: "shared-default-policy", + Shared: true, + IsDefault: true, + } - policy, err := policies.Create(networkClient, createOpts).Extract() - if err != nil { - panic(err) - } + policy, err := policies.Create(networkClient, createOpts).Extract() + if err != nil { + panic(err) + } - fmt.Printf("%+v\n", policy) + fmt.Printf("%+v\n", policy) Example to Update a QoS policy - shared := true - isDefault := false - opts := policies.UpdateOpts{ - Name: "new-name", - Shared: &shared, - IsDefault: &isDefault, - } + shared := true + isDefault := false + opts := policies.UpdateOpts{ + Name: "new-name", + Shared: &shared, + IsDefault: &isDefault, + } - policyID := "30a57f4a-336b-4382-8275-d708babd2241" + policyID := "30a57f4a-336b-4382-8275-d708babd2241" - policy, err := policies.Update(networkClient, policyID, opts).Extract() - if err != nil { - panic(err) - } + policy, err := policies.Update(networkClient, policyID, opts).Extract() + if err != nil { + panic(err) + } - fmt.Printf("%+v\n", policy) + fmt.Printf("%+v\n", policy) Example to Delete a QoS policy - policyID := "30a57f4a-336b-4382-8275-d708babd2241" + policyID := "30a57f4a-336b-4382-8275-d708babd2241" - err := policies.Delete(networkClient, policyID).ExtractErr() - if err != nil { - panic(err) - } + err := policies.Delete(networkClient, policyID).ExtractErr() + if err != nil { + panic(err) + } */ package policies diff --git a/openstack/networking/v2/extensions/qos/rules/doc.go b/openstack/networking/v2/extensions/qos/rules/doc.go index 8940e2f16e..9f87dd649d 100644 --- a/openstack/networking/v2/extensions/qos/rules/doc.go +++ b/openstack/networking/v2/extensions/qos/rules/doc.go @@ -3,234 +3,234 @@ Package rules provides the ability to retrieve and manage QoS policy rules throu Example of Listing BandwidthLimitRules - listOpts := rules.BandwidthLimitRulesListOpts{ - MaxKBps: 3000, - } + listOpts := rules.BandwidthLimitRulesListOpts{ + MaxKBps: 3000, + } - policyID := "501005fa-3b56-4061-aaca-3f24995112e1" + policyID := "501005fa-3b56-4061-aaca-3f24995112e1" - allPages, err := rules.ListBandwidthLimitRules(networkClient, policyID, listOpts).AllPages() - if err != nil { - panic(err) - } + allPages, err := rules.ListBandwidthLimitRules(networkClient, policyID, listOpts).AllPages() + if err != nil { + panic(err) + } - allBandwidthLimitRules, err := rules.ExtractBandwidthLimitRules(allPages) - if err != nil { - panic(err) - } + allBandwidthLimitRules, err := rules.ExtractBandwidthLimitRules(allPages) + if err != nil { + panic(err) + } - for _, bandwidthLimitRule := range allBandwidthLimitRules { - fmt.Printf("%+v\n", bandwidthLimitRule) - } + for _, bandwidthLimitRule := range allBandwidthLimitRules { + fmt.Printf("%+v\n", bandwidthLimitRule) + } Example of Getting a single BandwidthLimitRule - policyID := "501005fa-3b56-4061-aaca-3f24995112e1" - ruleID := "30a57f4a-336b-4382-8275-d708babd2241" + policyID := "501005fa-3b56-4061-aaca-3f24995112e1" + ruleID := "30a57f4a-336b-4382-8275-d708babd2241" - rule, err := rules.GetBandwidthLimitRule(networkClient, policyID, ruleID).ExtractBandwidthLimitRule() - if err != nil { - panic(err) - } + rule, err := rules.GetBandwidthLimitRule(networkClient, policyID, ruleID).ExtractBandwidthLimitRule() + if err != nil { + panic(err) + } - fmt.Printf("Rule: %+v\n", rule) + fmt.Printf("Rule: %+v\n", rule) Example of Creating a single BandwidthLimitRule - opts := rules.CreateBandwidthLimitRuleOpts{ - MaxKBps: 2000, - MaxBurstKBps: 200, - } + opts := rules.CreateBandwidthLimitRuleOpts{ + MaxKBps: 2000, + MaxBurstKBps: 200, + } - policyID := "501005fa-3b56-4061-aaca-3f24995112e1" + policyID := "501005fa-3b56-4061-aaca-3f24995112e1" - rule, err := rules.CreateBandwidthLimitRule(networkClient, policyID, opts).ExtractBandwidthLimitRule() - if err != nil { - panic(err) - } + rule, err := rules.CreateBandwidthLimitRule(networkClient, policyID, opts).ExtractBandwidthLimitRule() + if err != nil { + panic(err) + } - fmt.Printf("Rule: %+v\n", rule) + fmt.Printf("Rule: %+v\n", rule) Example of Updating a single BandwidthLimitRule - maxKBps := 500 - maxBurstKBps := 0 + maxKBps := 500 + maxBurstKBps := 0 - opts := rules.UpdateBandwidthLimitRuleOpts{ - MaxKBps: &maxKBps, - MaxBurstKBps: &maxBurstKBps, - } + opts := rules.UpdateBandwidthLimitRuleOpts{ + MaxKBps: &maxKBps, + MaxBurstKBps: &maxBurstKBps, + } - policyID := "501005fa-3b56-4061-aaca-3f24995112e1" - ruleID := "30a57f4a-336b-4382-8275-d708babd2241" + policyID := "501005fa-3b56-4061-aaca-3f24995112e1" + ruleID := "30a57f4a-336b-4382-8275-d708babd2241" - rule, err := rules.UpdateBandwidthLimitRule(networkClient, policyID, ruleID, opts).ExtractBandwidthLimitRule() - if err != nil { - panic(err) - } + rule, err := rules.UpdateBandwidthLimitRule(networkClient, policyID, ruleID, opts).ExtractBandwidthLimitRule() + if err != nil { + panic(err) + } - fmt.Printf("Rule: %+v\n", rule) + fmt.Printf("Rule: %+v\n", rule) Example of Deleting a single BandwidthLimitRule - policyID := "501005fa-3b56-4061-aaca-3f24995112e1" - ruleID := "30a57f4a-336b-4382-8275-d708babd2241" + policyID := "501005fa-3b56-4061-aaca-3f24995112e1" + ruleID := "30a57f4a-336b-4382-8275-d708babd2241" - err := rules.DeleteBandwidthLimitRule(fake.ServiceClient(), "501005fa-3b56-4061-aaca-3f24995112e1", "30a57f4a-336b-4382-8275-d708babd2241").ExtractErr() - if err != nil { - panic(err) - } + err := rules.DeleteBandwidthLimitRule(fake.ServiceClient(), "501005fa-3b56-4061-aaca-3f24995112e1", "30a57f4a-336b-4382-8275-d708babd2241").ExtractErr() + if err != nil { + panic(err) + } Example of Listing DSCP marking rules - listOpts := rules.DSCPMarkingRulesListOpts{} + listOpts := rules.DSCPMarkingRulesListOpts{} - policyID := "501005fa-3b56-4061-aaca-3f24995112e1" + policyID := "501005fa-3b56-4061-aaca-3f24995112e1" - allPages, err := rules.ListDSCPMarkingRules(networkClient, policyID, listOpts).AllPages() - if err != nil { - panic(err) - } + allPages, err := rules.ListDSCPMarkingRules(networkClient, policyID, listOpts).AllPages() + if err != nil { + panic(err) + } - allDSCPMarkingRules, err := rules.ExtractDSCPMarkingRules(allPages) - if err != nil { - panic(err) - } + allDSCPMarkingRules, err := rules.ExtractDSCPMarkingRules(allPages) + if err != nil { + panic(err) + } - for _, dscpMarkingRule := range allDSCPMarkingRules { - fmt.Printf("%+v\n", dscpMarkingRule) - } + for _, dscpMarkingRule := range allDSCPMarkingRules { + fmt.Printf("%+v\n", dscpMarkingRule) + } Example of Getting a single DSCPMarkingRule - policyID := "501005fa-3b56-4061-aaca-3f24995112e1" - ruleID := "30a57f4a-336b-4382-8275-d708babd2241" + policyID := "501005fa-3b56-4061-aaca-3f24995112e1" + ruleID := "30a57f4a-336b-4382-8275-d708babd2241" - rule, err := rules.GetDSCPMarkingRule(networkClient, policyID, ruleID).ExtractDSCPMarkingRule() - if err != nil { - panic(err) - } + rule, err := rules.GetDSCPMarkingRule(networkClient, policyID, ruleID).ExtractDSCPMarkingRule() + if err != nil { + panic(err) + } - fmt.Printf("Rule: %+v\n", rule) + fmt.Printf("Rule: %+v\n", rule) Example of Creating a single DSCPMarkingRule - opts := rules.CreateDSCPMarkingRuleOpts{ - DSCPMark: 20, - } + opts := rules.CreateDSCPMarkingRuleOpts{ + DSCPMark: 20, + } - policyID := "501005fa-3b56-4061-aaca-3f24995112e1" + policyID := "501005fa-3b56-4061-aaca-3f24995112e1" - rule, err := rules.CreateDSCPMarkingRule(networkClient, policyID, opts).ExtractDSCPMarkingRule() - if err != nil { - panic(err) - } + rule, err := rules.CreateDSCPMarkingRule(networkClient, policyID, opts).ExtractDSCPMarkingRule() + if err != nil { + panic(err) + } - fmt.Printf("Rule: %+v\n", rule) + fmt.Printf("Rule: %+v\n", rule) Example of Updating a single DSCPMarkingRule - dscpMark := 26 + dscpMark := 26 - opts := rules.UpdateDSCPMarkingRuleOpts{ - DSCPMark: &dscpMark, - } + opts := rules.UpdateDSCPMarkingRuleOpts{ + DSCPMark: &dscpMark, + } - policyID := "501005fa-3b56-4061-aaca-3f24995112e1" - ruleID := "30a57f4a-336b-4382-8275-d708babd2241" + policyID := "501005fa-3b56-4061-aaca-3f24995112e1" + ruleID := "30a57f4a-336b-4382-8275-d708babd2241" - rule, err := rules.UpdateDSCPMarkingRule(networkClient, policyID, ruleID, opts).ExtractDSCPMarkingRule() - if err != nil { - panic(err) - } + rule, err := rules.UpdateDSCPMarkingRule(networkClient, policyID, ruleID, opts).ExtractDSCPMarkingRule() + if err != nil { + panic(err) + } - fmt.Printf("Rule: %+v\n", rule) + fmt.Printf("Rule: %+v\n", rule) Example of Deleting a single DSCPMarkingRule - policyID := "501005fa-3b56-4061-aaca-3f24995112e1" - ruleID := "30a57f4a-336b-4382-8275-d708babd2241" + policyID := "501005fa-3b56-4061-aaca-3f24995112e1" + ruleID := "30a57f4a-336b-4382-8275-d708babd2241" - err := rules.DeleteDSCPMarkingRule(fake.ServiceClient(), "501005fa-3b56-4061-aaca-3f24995112e1", "30a57f4a-336b-4382-8275-d708babd2241").ExtractErr() - if err != nil { - panic(err) - } + err := rules.DeleteDSCPMarkingRule(fake.ServiceClient(), "501005fa-3b56-4061-aaca-3f24995112e1", "30a57f4a-336b-4382-8275-d708babd2241").ExtractErr() + if err != nil { + panic(err) + } Example of Listing MinimumBandwidthRules - listOpts := rules.MinimumBandwidthRulesListOpts{ - MinKBps: 3000, - } + listOpts := rules.MinimumBandwidthRulesListOpts{ + MinKBps: 3000, + } - policyID := "501005fa-3b56-4061-aaca-3f24995112e1" + policyID := "501005fa-3b56-4061-aaca-3f24995112e1" - allPages, err := rules.ListMinimumBandwidthRules(networkClient, policyID, listOpts).AllPages() - if err != nil { - panic(err) - } + allPages, err := rules.ListMinimumBandwidthRules(networkClient, policyID, listOpts).AllPages() + if err != nil { + panic(err) + } - allMinimumBandwidthRules, err := rules.ExtractMinimumBandwidthRules(allPages) - if err != nil { - panic(err) - } + allMinimumBandwidthRules, err := rules.ExtractMinimumBandwidthRules(allPages) + if err != nil { + panic(err) + } - for _, bandwidthLimitRule := range allMinimumBandwidthRules { - fmt.Printf("%+v\n", bandwidthLimitRule) - } + for _, bandwidthLimitRule := range allMinimumBandwidthRules { + fmt.Printf("%+v\n", bandwidthLimitRule) + } Example of Getting a single MinimumBandwidthRule - policyID := "501005fa-3b56-4061-aaca-3f24995112e1" - ruleID := "30a57f4a-336b-4382-8275-d708babd2241" + policyID := "501005fa-3b56-4061-aaca-3f24995112e1" + ruleID := "30a57f4a-336b-4382-8275-d708babd2241" - rule, err := rules.GetMinimumBandwidthRule(networkClient, policyID, ruleID).ExtractMinimumBandwidthRule() - if err != nil { - panic(err) - } + rule, err := rules.GetMinimumBandwidthRule(networkClient, policyID, ruleID).ExtractMinimumBandwidthRule() + if err != nil { + panic(err) + } - fmt.Printf("Rule: %+v\n", rule) + fmt.Printf("Rule: %+v\n", rule) Example of Creating a single MinimumBandwidthRule - opts := rules.CreateMinimumBandwidthRuleOpts{ - MinKBps: 2000, - } + opts := rules.CreateMinimumBandwidthRuleOpts{ + MinKBps: 2000, + } - policyID := "501005fa-3b56-4061-aaca-3f24995112e1" + policyID := "501005fa-3b56-4061-aaca-3f24995112e1" - rule, err := rules.CreateMinimumBandwidthRule(networkClient, policyID, opts).ExtractMinimumBandwidthRule() - if err != nil { - panic(err) - } + rule, err := rules.CreateMinimumBandwidthRule(networkClient, policyID, opts).ExtractMinimumBandwidthRule() + if err != nil { + panic(err) + } - fmt.Printf("Rule: %+v\n", rule) + fmt.Printf("Rule: %+v\n", rule) Example of Updating a single MinimumBandwidthRule - minKBps := 500 + minKBps := 500 - opts := rules.UpdateMinimumBandwidthRuleOpts{ - MinKBps: &minKBps, - } + opts := rules.UpdateMinimumBandwidthRuleOpts{ + MinKBps: &minKBps, + } - policyID := "501005fa-3b56-4061-aaca-3f24995112e1" - ruleID := "30a57f4a-336b-4382-8275-d708babd2241" + policyID := "501005fa-3b56-4061-aaca-3f24995112e1" + ruleID := "30a57f4a-336b-4382-8275-d708babd2241" - rule, err := rules.UpdateMinimumBandwidthRule(networkClient, policyID, ruleID, opts).ExtractMinimumBandwidthRule() - if err != nil { - panic(err) - } + rule, err := rules.UpdateMinimumBandwidthRule(networkClient, policyID, ruleID, opts).ExtractMinimumBandwidthRule() + if err != nil { + panic(err) + } - fmt.Printf("Rule: %+v\n", rule) + fmt.Printf("Rule: %+v\n", rule) Example of Deleting a single MinimumBandwidthRule - policyID := "501005fa-3b56-4061-aaca-3f24995112e1" - ruleID := "30a57f4a-336b-4382-8275-d708babd2241" + policyID := "501005fa-3b56-4061-aaca-3f24995112e1" + ruleID := "30a57f4a-336b-4382-8275-d708babd2241" - err := rules.DeleteMinimumBandwidthRule(fake.ServiceClient(), "501005fa-3b56-4061-aaca-3f24995112e1", "30a57f4a-336b-4382-8275-d708babd2241").ExtractErr() - if err != nil { - panic(err) - } + err := rules.DeleteMinimumBandwidthRule(fake.ServiceClient(), "501005fa-3b56-4061-aaca-3f24995112e1", "30a57f4a-336b-4382-8275-d708babd2241").ExtractErr() + if err != nil { + panic(err) + } */ package rules diff --git a/openstack/networking/v2/extensions/qos/ruletypes/doc.go b/openstack/networking/v2/extensions/qos/ruletypes/doc.go index f46fe83879..c36081b485 100644 --- a/openstack/networking/v2/extensions/qos/ruletypes/doc.go +++ b/openstack/networking/v2/extensions/qos/ruletypes/doc.go @@ -17,13 +17,13 @@ Example of Listing QoS rule types Example of Getting a single QoS rule type by it's name - ruleTypeName := "bandwidth_limit" + ruleTypeName := "bandwidth_limit" - ruleType, err := ruletypes.Get(networkClient, ruleTypeName).Extract() - if err != nil { - panic(err) - } + ruleType, err := ruletypes.Get(networkClient, ruleTypeName).Extract() + if err != nil { + panic(err) + } - fmt.Printf("%+v\n", ruleTypeName) + fmt.Printf("%+v\n", ruleTypeName) */ package ruletypes diff --git a/openstack/networking/v2/extensions/quotas/doc.go b/openstack/networking/v2/extensions/quotas/doc.go index cb39dc9939..2413e92351 100644 --- a/openstack/networking/v2/extensions/quotas/doc.go +++ b/openstack/networking/v2/extensions/quotas/doc.go @@ -3,45 +3,45 @@ Package quotas provides the ability to retrieve and manage Networking quotas thr Example to Get project quotas - projectID = "23d5d3f79dfa4f73b72b8b0b0063ec55" - quotasInfo, err := quotas.Get(networkClient, projectID).Extract() - if err != nil { - log.Fatal(err) - } + projectID = "23d5d3f79dfa4f73b72b8b0b0063ec55" + quotasInfo, err := quotas.Get(networkClient, projectID).Extract() + if err != nil { + log.Fatal(err) + } - fmt.Printf("quotas: %#v\n", quotasInfo) + fmt.Printf("quotas: %#v\n", quotasInfo) Example to Get a Detailed Quota Set - projectID = "23d5d3f79dfa4f73b72b8b0b0063ec55" - quotasInfo, err := quotas.GetDetail(networkClient, projectID).Extract() - if err != nil { - log.Fatal(err) - } + projectID = "23d5d3f79dfa4f73b72b8b0b0063ec55" + quotasInfo, err := quotas.GetDetail(networkClient, projectID).Extract() + if err != nil { + log.Fatal(err) + } - fmt.Printf("quotas: %#v\n", quotasInfo) + fmt.Printf("quotas: %#v\n", quotasInfo) Example to Update project quotas - projectID = "23d5d3f79dfa4f73b72b8b0b0063ec55" - - updateOpts := quotas.UpdateOpts{ - FloatingIP: gophercloud.IntToPointer(0), - Network: gophercloud.IntToPointer(-1), - Port: gophercloud.IntToPointer(5), - RBACPolicy: gophercloud.IntToPointer(10), - Router: gophercloud.IntToPointer(15), - SecurityGroup: gophercloud.IntToPointer(20), - SecurityGroupRule: gophercloud.IntToPointer(-1), - Subnet: gophercloud.IntToPointer(25), - SubnetPool: gophercloud.IntToPointer(0), - Trunk: gophercloud.IntToPointer(0), - } - quotasInfo, err := quotas.Update(networkClient, projectID) - if err != nil { - log.Fatal(err) - } - - fmt.Printf("quotas: %#v\n", quotasInfo) + projectID = "23d5d3f79dfa4f73b72b8b0b0063ec55" + + updateOpts := quotas.UpdateOpts{ + FloatingIP: gophercloud.IntToPointer(0), + Network: gophercloud.IntToPointer(-1), + Port: gophercloud.IntToPointer(5), + RBACPolicy: gophercloud.IntToPointer(10), + Router: gophercloud.IntToPointer(15), + SecurityGroup: gophercloud.IntToPointer(20), + SecurityGroupRule: gophercloud.IntToPointer(-1), + Subnet: gophercloud.IntToPointer(25), + SubnetPool: gophercloud.IntToPointer(0), + Trunk: gophercloud.IntToPointer(0), + } + quotasInfo, err := quotas.Update(networkClient, projectID) + if err != nil { + log.Fatal(err) + } + + fmt.Printf("quotas: %#v\n", quotasInfo) */ package quotas diff --git a/openstack/networking/v2/extensions/rbacpolicies/doc.go b/openstack/networking/v2/extensions/rbacpolicies/doc.go index f0ddbc0f67..5e69f21a28 100644 --- a/openstack/networking/v2/extensions/rbacpolicies/doc.go +++ b/openstack/networking/v2/extensions/rbacpolicies/doc.go @@ -15,17 +15,17 @@ before this feature was added. Example to Create a RBAC Policy - createOpts := rbacpolicies.CreateOpts{ - Action: rbacpolicies.ActionAccessShared, - ObjectType: "network", - TargetTenant: "6e547a3bcfe44702889fdeff3c3520c3", - ObjectID: "240d22bf-bd17-4238-9758-25f72610ecdc" - } - - rbacPolicy, err := rbacpolicies.Create(rbacClient, createOpts).Extract() - if err != nil { - panic(err) - } + createOpts := rbacpolicies.CreateOpts{ + Action: rbacpolicies.ActionAccessShared, + ObjectType: "network", + TargetTenant: "6e547a3bcfe44702889fdeff3c3520c3", + ObjectID: "240d22bf-bd17-4238-9758-25f72610ecdc" + } + + rbacPolicy, err := rbacpolicies.Create(rbacClient, createOpts).Extract() + if err != nil { + panic(err) + } Example to List RBAC Policies @@ -74,6 +74,5 @@ Example to Update a RBAC Policy if err != nil { panic(err) } - */ package rbacpolicies diff --git a/openstack/networking/v2/extensions/security/doc.go b/openstack/networking/v2/extensions/security/doc.go index 31f744ccd7..bb6e5b5a41 100644 --- a/openstack/networking/v2/extensions/security/doc.go +++ b/openstack/networking/v2/extensions/security/doc.go @@ -14,19 +14,19 @@ // The basic characteristics of Neutron Security Groups are: // // For ingress traffic (to an instance) -// - Only traffic matched with security group rules are allowed. -// - When there is no rule defined, all traffic is dropped. +// - Only traffic matched with security group rules are allowed. +// - When there is no rule defined, all traffic is dropped. // // For egress traffic (from an instance) -// - Only traffic matched with security group rules are allowed. -// - When there is no rule defined, all egress traffic are dropped. -// - When a new security group is created, rules to allow all egress traffic -// is automatically added. +// - Only traffic matched with security group rules are allowed. +// - When there is no rule defined, all egress traffic are dropped. +// - When a new security group is created, rules to allow all egress traffic +// is automatically added. // // "default security group" is defined for each tenant. -// - For the default security group a rule which allows intercommunication -// among hosts associated with the default security group is defined by default. -// - As a result, all egress traffic and intercommunication in the default -// group are allowed and all ingress from outside of the default group is -// dropped by default (in the default security group). +// - For the default security group a rule which allows intercommunication +// among hosts associated with the default security group is defined by default. +// - As a result, all egress traffic and intercommunication in the default +// group are allowed and all ingress from outside of the default group is +// dropped by default (in the default security group). package security diff --git a/openstack/networking/v2/extensions/vlantransparent/doc.go b/openstack/networking/v2/extensions/vlantransparent/doc.go index ae3216f412..bb10596dd9 100644 --- a/openstack/networking/v2/extensions/vlantransparent/doc.go +++ b/openstack/networking/v2/extensions/vlantransparent/doc.go @@ -4,33 +4,33 @@ with the vlan-transparent extension through the Neutron API. Example of Listing Networks with the vlan-transparent extension - iTrue := true - networkListOpts := networks.ListOpts{} - listOpts := vlantransparent.ListOptsExt{ - ListOptsBuilder: networkListOpts, - VLANTransparent: &iTrue, - } - - type NetworkWithVLANTransparentExt struct { - networks.Network - vlantransparent.NetworkVLANTransparentExt - } - - var allNetworks []NetworkWithVLANTransparentExt - - allPages, err := networks.List(networkClient, listOpts).AllPages() - if err != nil { - panic(err) - } - - err = networks.ExtractNetworksInto(allPages, &allNetworks) - if err != nil { - panic(err) - } - - for _, network := range allNetworks { - fmt.Printf("%+v\n", network) - } + iTrue := true + networkListOpts := networks.ListOpts{} + listOpts := vlantransparent.ListOptsExt{ + ListOptsBuilder: networkListOpts, + VLANTransparent: &iTrue, + } + + type NetworkWithVLANTransparentExt struct { + networks.Network + vlantransparent.NetworkVLANTransparentExt + } + + var allNetworks []NetworkWithVLANTransparentExt + + allPages, err := networks.List(networkClient, listOpts).AllPages() + if err != nil { + panic(err) + } + + err = networks.ExtractNetworksInto(allPages, &allNetworks) + if err != nil { + panic(err) + } + + for _, network := range allNetworks { + fmt.Printf("%+v\n", network) + } Example of Getting a Network with the vlan-transparent extension diff --git a/openstack/networking/v2/extensions/vpnaas/ikepolicies/doc.go b/openstack/networking/v2/extensions/vpnaas/ikepolicies/doc.go index ee44279afa..72720fe3ae 100644 --- a/openstack/networking/v2/extensions/vpnaas/ikepolicies/doc.go +++ b/openstack/networking/v2/extensions/vpnaas/ikepolicies/doc.go @@ -2,7 +2,6 @@ Package ikepolicies allows management and retrieval of IKE policies in the OpenStack Networking Service. - Example to Create an IKE policy createOpts := ikepolicies.CreateOpts{ @@ -24,7 +23,6 @@ Example to Show the details of a specific IKE policy by ID panic(err) } - Example to Delete a Policy err := ikepolicies.Delete(client, "5291b189-fd84-46e5-84bd-78f40c05d69c").ExtractErr() @@ -47,7 +45,6 @@ Example to Update an IKE policy panic(err) } - Example to List IKE policies allPages, err := ikepolicies.List(client, nil).AllPages() @@ -59,6 +56,5 @@ Example to List IKE policies if err != nil { panic(err) } - */ package ikepolicies diff --git a/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/doc.go b/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/doc.go index 91d5451a6e..1e9303eada 100644 --- a/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/doc.go +++ b/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/doc.go @@ -51,6 +51,5 @@ Example to List IPSec policies if err != nil { panic(err) } - */ package ipsecpolicies diff --git a/openstack/networking/v2/extensions/vpnaas/services/doc.go b/openstack/networking/v2/extensions/vpnaas/services/doc.go index 6bd3236c84..5cafb113f4 100644 --- a/openstack/networking/v2/extensions/vpnaas/services/doc.go +++ b/openstack/networking/v2/extensions/vpnaas/services/doc.go @@ -63,6 +63,5 @@ Example to Show the details of a specific Service by ID if err != nil { panic(err) } - */ package services diff --git a/openstack/networking/v2/extensions/vpnaas/siteconnections/doc.go b/openstack/networking/v2/extensions/vpnaas/siteconnections/doc.go index 66befd3ba2..1b9c0842e4 100644 --- a/openstack/networking/v2/extensions/vpnaas/siteconnections/doc.go +++ b/openstack/networking/v2/extensions/vpnaas/siteconnections/doc.go @@ -2,27 +2,26 @@ Package siteconnections allows management and retrieval of IPSec site connections in the OpenStack Networking Service. +# Example to create an IPSec site connection -Example to create an IPSec site connection - -createOpts := siteconnections.CreateOpts{ - Name: "Connection1", - PSK: "secret", - Initiator: siteconnections.InitiatorBiDirectional, - AdminStateUp: gophercloud.Enabled, - IPSecPolicyID: "4ab0a72e-64ef-4809-be43-c3f7e0e5239b", - PeerEPGroupID: "5f5801b1-b383-4cf0-bf61-9e85d4044b2d", - IKEPolicyID: "47a880f9-1da9-468c-b289-219c9eca78f0", - VPNServiceID: "692c1ec8-a7cd-44d9-972b-8ed3fe4cc476", - LocalEPGroupID: "498bb96a-1517-47ea-b1eb-c4a53db46a16", - PeerAddress: "172.24.4.233", - PeerID: "172.24.4.233", - MTU: 1500, - } - connection, err := siteconnections.Create(client, createOpts).Extract() - if err != nil { - panic(err) - } + createOpts := siteconnections.CreateOpts{ + Name: "Connection1", + PSK: "secret", + Initiator: siteconnections.InitiatorBiDirectional, + AdminStateUp: gophercloud.Enabled, + IPSecPolicyID: "4ab0a72e-64ef-4809-be43-c3f7e0e5239b", + PeerEPGroupID: "5f5801b1-b383-4cf0-bf61-9e85d4044b2d", + IKEPolicyID: "47a880f9-1da9-468c-b289-219c9eca78f0", + VPNServiceID: "692c1ec8-a7cd-44d9-972b-8ed3fe4cc476", + LocalEPGroupID: "498bb96a-1517-47ea-b1eb-c4a53db46a16", + PeerAddress: "172.24.4.233", + PeerID: "172.24.4.233", + MTU: 1500, + } + connection, err := siteconnections.Create(client, createOpts).Extract() + if err != nil { + panic(err) + } Example to Show the details of a specific IPSec site connection by ID @@ -63,6 +62,5 @@ Example to Update an IPSec site connection if err != nil { panic(err) } - */ package siteconnections diff --git a/openstack/objectstorage/v1/accounts/doc.go b/openstack/objectstorage/v1/accounts/doc.go index 0fa1c083a2..06290c6bd1 100644 --- a/openstack/objectstorage/v1/accounts/doc.go +++ b/openstack/objectstorage/v1/accounts/doc.go @@ -24,6 +24,5 @@ Example to Update an Account updateResult, err := accounts.Update(objectStorageClient, updateOpts).Extract() fmt.Printf("%+v\n", updateResult) - */ package accounts diff --git a/openstack/orchestration/v1/resourcetypes/doc.go b/openstack/orchestration/v1/resourcetypes/doc.go index cfc6076530..b964b33e42 100644 --- a/openstack/orchestration/v1/resourcetypes/doc.go +++ b/openstack/orchestration/v1/resourcetypes/doc.go @@ -5,17 +5,17 @@ customised to use as provider templates. Example of listing available resource types: - listOpts := resourcetypes.ListOpts{ - SupportStatus: resourcetypes.SupportStatusSupported, - } + listOpts := resourcetypes.ListOpts{ + SupportStatus: resourcetypes.SupportStatusSupported, + } - resourceTypes, err := resourcetypes.List(client, listOpts).Extract() - if err != nil { - panic(err) - } - fmt.Println("Get Resource Type List") - for _, rt := range resTypes { - fmt.Println(rt.ResourceType) - } + resourceTypes, err := resourcetypes.List(client, listOpts).Extract() + if err != nil { + panic(err) + } + fmt.Println("Get Resource Type List") + for _, rt := range resTypes { + fmt.Println(rt.ResourceType) + } */ package resourcetypes diff --git a/openstack/orchestration/v1/stackevents/doc.go b/openstack/orchestration/v1/stackevents/doc.go index 0787858391..ff78fcc512 100644 --- a/openstack/orchestration/v1/stackevents/doc.go +++ b/openstack/orchestration/v1/stackevents/doc.go @@ -5,15 +5,15 @@ updating and abandoning. Example for list events for a stack - pages, err := stackevents.List(client, stack.Name, stack.ID, nil).AllPages() - if err != nil { - panic(err) - } - events, err := stackevents.ExtractEvents(pages) - if err != nil { - panic(err) - } - fmt.Println("Get Event List") - fmt.Println(events) + pages, err := stackevents.List(client, stack.Name, stack.ID, nil).AllPages() + if err != nil { + panic(err) + } + events, err := stackevents.ExtractEvents(pages) + if err != nil { + panic(err) + } + fmt.Println("Get Event List") + fmt.Println(events) */ package stackevents diff --git a/openstack/orchestration/v1/stackresources/doc.go b/openstack/orchestration/v1/stackresources/doc.go index ae282dc08c..2f2be2fd6e 100644 --- a/openstack/orchestration/v1/stackresources/doc.go +++ b/openstack/orchestration/v1/stackresources/doc.go @@ -6,66 +6,65 @@ balancer, some configuration management system, and so forth). Example of get resource information in stack - rsrc_result := stackresources.Get(client, stack.Name, stack.ID, rsrc.Name) - if rsrc_result.Err != nil { - panic(rsrc_result.Err) - } - rsrc, err := rsrc_result.Extract() - if err != nil { - panic(err) - } + rsrc_result := stackresources.Get(client, stack.Name, stack.ID, rsrc.Name) + if rsrc_result.Err != nil { + panic(rsrc_result.Err) + } + rsrc, err := rsrc_result.Extract() + if err != nil { + panic(err) + } Example for list stack resources - all_stack_rsrc_pages, err := stackresources.List(client, stack.Name, stack.ID, nil).AllPages() - if err != nil { - panic(err) - } + all_stack_rsrc_pages, err := stackresources.List(client, stack.Name, stack.ID, nil).AllPages() + if err != nil { + panic(err) + } - all_stack_rsrcs, err := stackresources.ExtractResources(all_stack_rsrc_pages) - if err != nil { - panic(err) - } - - fmt.Println("Resource List:") - for _, rsrc := range all_stack_rsrcs { - // Get information of a resource in stack - rsrc_result := stackresources.Get(client, stack.Name, stack.ID, rsrc.Name) - if rsrc_result.Err != nil { - panic(rsrc_result.Err) - } - rsrc, err := rsrc_result.Extract() - if err != nil { - panic(err) - } - fmt.Println("Resource Name: ", rsrc.Name, ", Physical ID: ", rsrc.PhysicalID, ", Status: ", rsrc.Status) - } + all_stack_rsrcs, err := stackresources.ExtractResources(all_stack_rsrc_pages) + if err != nil { + panic(err) + } + fmt.Println("Resource List:") + for _, rsrc := range all_stack_rsrcs { + // Get information of a resource in stack + rsrc_result := stackresources.Get(client, stack.Name, stack.ID, rsrc.Name) + if rsrc_result.Err != nil { + panic(rsrc_result.Err) + } + rsrc, err := rsrc_result.Extract() + if err != nil { + panic(err) + } + fmt.Println("Resource Name: ", rsrc.Name, ", Physical ID: ", rsrc.PhysicalID, ", Status: ", rsrc.Status) + } Example for get resource type schema - schema_result := stackresources.Schema(client, "OS::Heat::Stack") - if schema_result.Err != nil { - panic(schema_result.Err) - } - schema, err := schema_result.Extract() - if err != nil { - panic(err) - } - fmt.Println("Schema for resource type OS::Heat::Stack") - fmt.Println(schema.SupportStatus) + schema_result := stackresources.Schema(client, "OS::Heat::Stack") + if schema_result.Err != nil { + panic(schema_result.Err) + } + schema, err := schema_result.Extract() + if err != nil { + panic(err) + } + fmt.Println("Schema for resource type OS::Heat::Stack") + fmt.Println(schema.SupportStatus) Example for get resource type Template - tmp_result := stackresources.Template(client, "OS::Heat::Stack") - if tmp_result.Err != nil { - panic(tmp_result.Err) - } - tmp, err := tmp_result.Extract() - if err != nil { - panic(err) - } - fmt.Println("Template for resource type OS::Heat::Stack") - fmt.Println(string(tmp)) + tmp_result := stackresources.Template(client, "OS::Heat::Stack") + if tmp_result.Err != nil { + panic(tmp_result.Err) + } + tmp, err := tmp_result.Extract() + if err != nil { + panic(err) + } + fmt.Println("Template for resource type OS::Heat::Stack") + fmt.Println(string(tmp)) */ package stackresources diff --git a/openstack/orchestration/v1/stacks/doc.go b/openstack/orchestration/v1/stacks/doc.go index 33fc3271c4..78bfa8d7b9 100644 --- a/openstack/orchestration/v1/stacks/doc.go +++ b/openstack/orchestration/v1/stacks/doc.go @@ -7,109 +7,111 @@ application framework or component specified (in the template). A stack is a running instance of a template. The result of creating a stack is a deployment of the application framework or component. -Prepare required import packages +# Prepare required import packages import ( - "fmt" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack" - "github.com/gophercloud/gophercloud/openstack/orchestration/v1/stacks" + + "fmt" + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack" + "github.com/gophercloud/gophercloud/openstack/orchestration/v1/stacks" + ) Example of Preparing Orchestration client: - client, err := openstack.NewOrchestrationV1(provider, gophercloud.EndpointOpts{Region: "RegionOne"}) + client, err := openstack.NewOrchestrationV1(provider, gophercloud.EndpointOpts{Region: "RegionOne"}) Example of List Stack: - all_stack_pages, err := stacks.List(client, nil).AllPages() - if err != nil { - panic(err) - } - all_stacks, err := stacks.ExtractStacks(all_stack_pages) - if err != nil { - panic(err) - } + all_stack_pages, err := stacks.List(client, nil).AllPages() + if err != nil { + panic(err) + } - for _, stack := range all_stacks { - fmt.Printf("%+v\n", stack) - } + all_stacks, err := stacks.ExtractStacks(all_stack_pages) + if err != nil { + panic(err) + } + for _, stack := range all_stacks { + fmt.Printf("%+v\n", stack) + } Example to Create an Stack - // Create Template - t := make(map[string]interface{}) - f, err := ioutil.ReadFile("template.yaml") - if err != nil { - panic(err) - } - err = yaml.Unmarshal(f, t) - if err != nil { - panic(err) - } - - template := &stacks.Template{} - template.TE = stacks.TE{ - Bin: f, - } - // Create Environment if needed - t_env := make(map[string]interface{}) - f_env, err := ioutil.ReadFile("env.yaml") - if err != nil { - panic(err) - } - err = yaml.Unmarshal(f_env, t_env) - if err != nil { - panic(err) - } - - env := &stacks.Environment{} - env.TE = stacks.TE{ - Bin: f_env, - } - - // Remember, the priority of parameters you given through - // Parameters is higher than the parameters you provided in EnvironmentOpts. - params := make(map[string]string) - params["number_of_nodes"] = 1 - tags := []string{"example-stack"} - createOpts := &stacks.CreateOpts{ - // The name of the stack. It must start with an alphabetic character. - Name: "testing_group", - // A structure that contains either the template file or url. Call the - // associated methods to extract the information relevant to send in a create request. - TemplateOpts: template, - // A structure that contains details for the environment of the stack. - EnvironmentOpts: env, - // User-defined parameters to pass to the template. - Parameters: params, - // A list of tags to assosciate with the Stack - Tags: tags, - } - - r := stacks.Create(client, createOpts) - //dcreated_stack := stacks.CreatedStack() - if r.Err != nil { - panic(r.Err) - } - created_stack, err := r.Extract() - if err != nil { - panic(err) - } - fmt.Printf("Created Stack: %v", created_stack.ID) + // Create Template + t := make(map[string]interface{}) + f, err := ioutil.ReadFile("template.yaml") + if err != nil { + panic(err) + } + err = yaml.Unmarshal(f, t) + if err != nil { + panic(err) + } + + template := &stacks.Template{} + template.TE = stacks.TE{ + Bin: f, + } + // Create Environment if needed + t_env := make(map[string]interface{}) + f_env, err := ioutil.ReadFile("env.yaml") + if err != nil { + panic(err) + } + err = yaml.Unmarshal(f_env, t_env) + if err != nil { + panic(err) + } + + env := &stacks.Environment{} + env.TE = stacks.TE{ + Bin: f_env, + } + + // Remember, the priority of parameters you given through + // Parameters is higher than the parameters you provided in EnvironmentOpts. + params := make(map[string]string) + params["number_of_nodes"] = 1 + tags := []string{"example-stack"} + createOpts := &stacks.CreateOpts{ + // The name of the stack. It must start with an alphabetic character. + Name: "testing_group", + // A structure that contains either the template file or url. Call the + // associated methods to extract the information relevant to send in a create request. + TemplateOpts: template, + // A structure that contains details for the environment of the stack. + EnvironmentOpts: env, + // User-defined parameters to pass to the template. + Parameters: params, + // A list of tags to assosciate with the Stack + Tags: tags, + } + + r := stacks.Create(client, createOpts) + //dcreated_stack := stacks.CreatedStack() + if r.Err != nil { + panic(r.Err) + } + created_stack, err := r.Extract() + if err != nil { + panic(err) + } + fmt.Printf("Created Stack: %v", created_stack.ID) Example for Get Stack - get_result := stacks.Get(client, stackName, created_stack.ID) - if get_result.Err != nil { - panic(get_result.Err) - } - stack, err := get_result.Extract() - if err != nil { - panic(err) - } - fmt.Println("Get Stack: Name: ", stack.Name, ", ID: ", stack.ID, ", Status: ", stack.Status) + get_result := stacks.Get(client, stackName, created_stack.ID) + if get_result.Err != nil { + panic(get_result.Err) + } + stack, err := get_result.Extract() + if err != nil { + panic(err) + } + fmt.Println("Get Stack: Name: ", stack.Name, ", ID: ", stack.ID, ", Status: ", stack.Status) Example for Find Stack @@ -125,15 +127,15 @@ Example for Find Stack Example for Delete Stack - del_r := stacks.Delete(client, stackName, created_stack.ID) - if del_r.Err != nil { - panic(del_r.Err) - } - fmt.Println("Deleted Stack: ", stackName) + del_r := stacks.Delete(client, stackName, created_stack.ID) + if del_r.Err != nil { + panic(del_r.Err) + } + fmt.Println("Deleted Stack: ", stackName) Summary of Behavior Between Stack Update and UpdatePatch Methods : -Function | Test Case | Result +# Function | Test Case | Result Update() | Template AND Parameters WITH Conflict | Parameter takes priority, parameters are set in raw_template.environment overlay Update() | Template ONLY | Template updates, raw_template.environment overlay is removed diff --git a/openstack/orchestration/v1/stacks/requests.go b/openstack/orchestration/v1/stacks/requests.go index a17cbd5f98..620e7576dc 100644 --- a/openstack/orchestration/v1/stacks/requests.go +++ b/openstack/orchestration/v1/stacks/requests.go @@ -404,7 +404,8 @@ func toStackUpdateMap(opts UpdateOpts) (map[string]interface{}, error) { } // Update accepts an UpdateOpts struct and updates an existing stack using the -// http PUT verb with the values provided. opts.TemplateOpts is required. +// +// http PUT verb with the values provided. opts.TemplateOpts is required. func Update(c *gophercloud.ServiceClient, stackName, stackID string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToStackUpdateMap() if err != nil { @@ -417,7 +418,8 @@ func Update(c *gophercloud.ServiceClient, stackName, stackID string, opts Update } // Update accepts an UpdateOpts struct and updates an existing stack using the -// http PATCH verb with the values provided. opts.TemplateOpts is not required. +// +// http PATCH verb with the values provided. opts.TemplateOpts is not required. func UpdatePatch(c *gophercloud.ServiceClient, stackName, stackID string, opts UpdatePatchOptsBuilder) (r UpdateResult) { b, err := opts.ToStackUpdatePatchMap() if err != nil { diff --git a/openstack/orchestration/v1/stacktemplates/doc.go b/openstack/orchestration/v1/stacktemplates/doc.go index 52fe62096b..76d6475e71 100644 --- a/openstack/orchestration/v1/stacktemplates/doc.go +++ b/openstack/orchestration/v1/stacktemplates/doc.go @@ -9,30 +9,29 @@ specific application stack. Example to get stack template - temp, err := stacktemplates.Get(client, stack.Name, stack.ID).Extract() - if err != nil { - panic(err) - } - fmt.Println("Get Stack Template for Stack ", stack.Name) - fmt.Println(string(temp)) + temp, err := stacktemplates.Get(client, stack.Name, stack.ID).Extract() + if err != nil { + panic(err) + } + fmt.Println("Get Stack Template for Stack ", stack.Name) + fmt.Println(string(temp)) Example to validate stack template - f2, err := ioutil.ReadFile("template.err.yaml") - if err != nil { - panic(err) - } - fmt.Println(string(f2)) - validateOpts := &stacktemplates.ValidateOpts{ - Template: string(f2), - } - validate_result, err := stacktemplates.Validate(client, validateOpts).Extract() - if err != nil { - // If validate failed, you will get error message here - fmt.Println("Validate failed: ", err.Error()) - } else { - fmt.Println(validate_result.Parameters) - } - + f2, err := ioutil.ReadFile("template.err.yaml") + if err != nil { + panic(err) + } + fmt.Println(string(f2)) + validateOpts := &stacktemplates.ValidateOpts{ + Template: string(f2), + } + validate_result, err := stacktemplates.Validate(client, validateOpts).Extract() + if err != nil { + // If validate failed, you will get error message here + fmt.Println("Validate failed: ", err.Error()) + } else { + fmt.Println(validate_result.Parameters) + } */ package stacktemplates diff --git a/openstack/placement/v1/resourceproviders/doc.go b/openstack/placement/v1/resourceproviders/doc.go index bb26372812..27feb28d76 100644 --- a/openstack/placement/v1/resourceproviders/doc.go +++ b/openstack/placement/v1/resourceproviders/doc.go @@ -88,6 +88,5 @@ Example to get resource providers allocations if err != nil { panic(err) } - */ package resourceproviders diff --git a/openstack/sharedfilesystems/v2/shares/doc.go b/openstack/sharedfilesystems/v2/shares/doc.go index 731dd85e2d..e9a8a35246 100644 --- a/openstack/sharedfilesystems/v2/shares/doc.go +++ b/openstack/sharedfilesystems/v2/shares/doc.go @@ -6,6 +6,7 @@ For more information, see: https://docs.openstack.org/api-ref/shared-file-system/ Example to Revert a Share to a Snapshot ID + opts := &shares.RevertOpts{ // snapshot ID to revert to SnapshotID: "ddeac769-9742-497f-b985-5bcfa94a3fd6", @@ -17,6 +18,7 @@ Example to Revert a Share to a Snapshot ID } Example to Reset a Share Status + opts := &shares.ResetStatusOpts{ // a new Share Status Status: "available", @@ -28,6 +30,7 @@ Example to Reset a Share Status } Example to Force Delete a Share + manilaClient.Microversion = "2.7" err := shares.ForceDelete(manilaClient, shareID).ExtractErr() if err != nil { @@ -35,11 +38,11 @@ Example to Force Delete a Share } Example to Unmanage a Share + manilaClient.Microversion = "2.7" err := shares.Unmanage(manilaClient, shareID).ExtractErr() if err != nil { panic(err) } - */ package shares diff --git a/openstack/utils/testing/doc.go b/openstack/utils/testing/doc.go index 66ecc07982..20d095afe4 100644 --- a/openstack/utils/testing/doc.go +++ b/openstack/utils/testing/doc.go @@ -1,2 +1,2 @@ -//utils +// utils package testing diff --git a/openstack/workflow/v2/crontriggers/doc.go b/openstack/workflow/v2/crontriggers/doc.go index 46164cad56..782899b3b0 100644 --- a/openstack/workflow/v2/crontriggers/doc.go +++ b/openstack/workflow/v2/crontriggers/doc.go @@ -4,7 +4,7 @@ Package crontriggers provides interaction with the cron triggers API in the Open Cron trigger is an object that allows to run Mistral workflows according to a time pattern (Unix crontab patterns format). Once a trigger is created it will run a specified workflow according to its properties: pattern, first_execution_time and remaining_executions. -List cron triggers +# List cron triggers To filter cron triggers from a list request, you can use advanced filters with special FilterType to check for equality, non equality, values greater or lower, etc. Default Filter checks equality, but you can override it with provided filter type. @@ -68,6 +68,5 @@ Delete a cron trigger if res.Err != nil { panic(res.Err) } - */ package crontriggers diff --git a/openstack/workflow/v2/executions/doc.go b/openstack/workflow/v2/executions/doc.go index cb1f4804ba..1633ff0ddb 100644 --- a/openstack/workflow/v2/executions/doc.go +++ b/openstack/workflow/v2/executions/doc.go @@ -5,7 +5,7 @@ An execution is a one-shot execution of a specific workflow. Each execution cont An execution represents also the execution of a cron trigger. Each run of a cron trigger will generate an execution. -List executions +# List executions To filter executions from a list request, you can use advanced filters with special FilterType to check for equality, non equality, values greater or lower, etc. Default Filter checks equality, but you can override it with provided filter type. @@ -65,6 +65,5 @@ Delete an execution if res.Err != nil { panic(res.Err) } - */ package executions diff --git a/openstack/workflow/v2/workflows/doc.go b/openstack/workflow/v2/workflows/doc.go index af3c400d5d..23aa46d97c 100644 --- a/openstack/workflow/v2/workflows/doc.go +++ b/openstack/workflow/v2/workflows/doc.go @@ -37,31 +37,31 @@ Get a workflow Create a workflow - workflowDefinition := `--- - version: '2.0' - - workflow_echo: - description: Simple workflow example - type: direct - input: - - msg - - tasks: - test: - action: std.echo output="<% $.msg %>"` - - createOpts := &workflows.CreateOpts{ - Definition: strings.NewReader(workflowDefinition), - Scope: "private", - Namespace: "some-namespace", - } - - workflow, err := workflows.Create(mistralClient, createOpts).Extract() - if err != nil { - panic(err) - } + workflowDefinition := `--- + version: '2.0' + + workflow_echo: + description: Simple workflow example + type: direct + input: + - msg + + tasks: + test: + action: std.echo output="<% $.msg %>"` + + createOpts := &workflows.CreateOpts{ + Definition: strings.NewReader(workflowDefinition), + Scope: "private", + Namespace: "some-namespace", + } + + workflow, err := workflows.Create(mistralClient, createOpts).Extract() + if err != nil { + panic(err) + } - fmt.Printf("%+v\n", workflow) + fmt.Printf("%+v\n", workflow) Delete a workflow diff --git a/params.go b/params.go index 6282894d3a..17b200cd23 100644 --- a/params.go +++ b/params.go @@ -15,17 +15,17 @@ 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") + 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 @@ -401,22 +401,22 @@ 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"` - } + type struct Something { + Bar string `h:"x_bar"` + Baz int `h:"lorem_ipsum"` + } - instance := Something{ - Bar: "AAA", - Baz: "BBB", - } + instance := Something{ + Bar: "AAA", + Baz: "BBB", + } will be converted into: - map[string]string{ - "x_bar": "AAA", - "lorem_ipsum": "BBB", - } + 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. From 5e6cb9ec595f5a4d25cbced1b98dedf1b9679087 Mon Sep 17 00:00:00 2001 From: Matt Vinall <boyvinall@gmail.com> Date: Wed, 24 Aug 2022 13:54:02 +0100 Subject: [PATCH 124/360] Add CreatedAt to ports.Port Signed-off-by: Matt Vinall <boyvinall@gmail.com> --- openstack/networking/v2/ports/results.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/openstack/networking/v2/ports/results.go b/openstack/networking/v2/ports/results.go index 7f8d5ef251..05ba595619 100644 --- a/openstack/networking/v2/ports/results.go +++ b/openstack/networking/v2/ports/results.go @@ -113,6 +113,9 @@ type Port struct { // RevisionNumber optionally set via extensions/standard-attr-revisions RevisionNumber int `json:"revision_number"` + // Timestamp when the port was created + CreatedAt time.Time `json:"created_at"` + // Timestamp when the port was last updated UpdatedAt time.Time `json:"updated_at"` } From c67ff59a5539097ffaf479bf5db738d18eb0ece9 Mon Sep 17 00:00:00 2001 From: nikParasyr <nik.parasyr@protonmail.com> Date: Thu, 25 Aug 2022 14:02:16 +0200 Subject: [PATCH 125/360] Add description to flavor Add description to flavors which has been added as an optional attribute since nova 2.55 (Queens). --- acceptance/openstack/compute/v2/compute.go | 16 ++++-- openstack/compute/v2/flavors/requests.go | 5 ++ openstack/compute/v2/flavors/results.go | 5 ++ .../v2/flavors/testing/requests_test.go | 54 ++++++++++--------- 4 files changed, 51 insertions(+), 29 deletions(-) diff --git a/acceptance/openstack/compute/v2/compute.go b/acceptance/openstack/compute/v2/compute.go index 21c94192d0..c34a5f5774 100644 --- a/acceptance/openstack/compute/v2/compute.go +++ b/acceptance/openstack/compute/v2/compute.go @@ -208,15 +208,20 @@ func CreateDefaultRule(t *testing.T, client *gophercloud.ServiceClient) (dsr.Def // An error will be returned if the flavor could not be created. func CreateFlavor(t *testing.T, client *gophercloud.ServiceClient) (*flavors.Flavor, error) { flavorName := tools.RandomString("flavor_", 5) + flavorDescription := fmt.Sprintf("I am %s and i am a yummy flavor", flavorName) + + // Microversion 2.55 is required to add description to flavor + client.Microversion = "2.55" t.Logf("Attempting to create flavor %s", flavorName) isPublic := true createOpts := flavors.CreateOpts{ - Name: flavorName, - RAM: 1, - VCPUs: 1, - Disk: gophercloud.IntToPointer(1), - IsPublic: &isPublic, + Name: flavorName, + RAM: 1, + VCPUs: 1, + Disk: gophercloud.IntToPointer(1), + IsPublic: &isPublic, + Description: flavorDescription, } flavor, err := flavors.Create(client, createOpts).Extract() @@ -231,6 +236,7 @@ func CreateFlavor(t *testing.T, client *gophercloud.ServiceClient) (*flavors.Fla th.AssertEquals(t, flavor.Disk, 1) th.AssertEquals(t, flavor.VCPUs, 1) th.AssertEquals(t, flavor.IsPublic, true) + th.AssertEquals(t, flavor.Description, flavorDescription) return flavor, nil } diff --git a/openstack/compute/v2/flavors/requests.go b/openstack/compute/v2/flavors/requests.go index 2c527b79fe..61a8b7dc4a 100644 --- a/openstack/compute/v2/flavors/requests.go +++ b/openstack/compute/v2/flavors/requests.go @@ -128,6 +128,11 @@ type CreateOpts struct { // Ephemeral is the amount of ephemeral disk space, measured in GB. Ephemeral *int `json:"OS-FLV-EXT-DATA:ephemeral,omitempty"` + + // Description is a free form description of the flavor. Limited to + // 65535 characters in length. Only printable characters are allowed. + // New in version 2.55 + Description string `json:"description,omitempty"` } // ToFlavorCreateMap constructs a request body from CreateOpts. diff --git a/openstack/compute/v2/flavors/results.go b/openstack/compute/v2/flavors/results.go index 92fe1b1809..3cad7747be 100644 --- a/openstack/compute/v2/flavors/results.go +++ b/openstack/compute/v2/flavors/results.go @@ -69,6 +69,11 @@ type Flavor struct { // Ephemeral is the amount of ephemeral disk space, measured in GB. Ephemeral int `json:"OS-FLV-EXT-DATA:ephemeral"` + + // Description is a free form description of the flavor. Limited to + // 65535 characters in length. Only printable characters are allowed. + // New in version 2.55 + Description string `json:"description"` } func (r *Flavor) UnmarshalJSON(b []byte) error { diff --git a/openstack/compute/v2/flavors/testing/requests_test.go b/openstack/compute/v2/flavors/testing/requests_test.go index 3dddcd34fe..f045449aea 100644 --- a/openstack/compute/v2/flavors/testing/requests_test.go +++ b/openstack/compute/v2/flavors/testing/requests_test.go @@ -38,7 +38,8 @@ func TestListFlavors(t *testing.T) { "ram": 9216000, "swap":"", "os-flavor-access:is_public": true, - "OS-FLV-EXT-DATA:ephemeral": 10 + "OS-FLV-EXT-DATA:ephemeral": 10, + "description": "foo" }, { "id": "2", @@ -87,7 +88,7 @@ func TestListFlavors(t *testing.T) { } expected := []flavors.Flavor{ - {ID: "1", Name: "m1.tiny", VCPUs: 1, Disk: 1, RAM: 9216000, Swap: 0, IsPublic: true, Ephemeral: 10}, + {ID: "1", Name: "m1.tiny", VCPUs: 1, Disk: 1, RAM: 9216000, Swap: 0, IsPublic: true, Ephemeral: 10, Description: "foo"}, {ID: "2", Name: "m1.small", VCPUs: 1, Disk: 20, RAM: 2048, Swap: 1000, IsPublic: true, Ephemeral: 0}, {ID: "3", Name: "m1.medium", VCPUs: 2, Disk: 40, RAM: 4096, Swap: 1000, IsPublic: false, Ephemeral: 0}, } @@ -124,7 +125,8 @@ func TestGetFlavor(t *testing.T) { "ram": 512, "vcpus": 1, "rxtx_factor": 1, - "swap": "" + "swap": "", + "description": "foo" } } `) @@ -136,13 +138,14 @@ func TestGetFlavor(t *testing.T) { } expected := &flavors.Flavor{ - ID: "1", - Name: "m1.tiny", - Disk: 1, - RAM: 512, - VCPUs: 1, - RxTxFactor: 1, - Swap: 0, + ID: "1", + Name: "m1.tiny", + Disk: 1, + RAM: 512, + VCPUs: 1, + RxTxFactor: 1, + Swap: 0, + Description: "foo", } if !reflect.DeepEqual(expected, actual) { t.Errorf("Expected %#v, but was %#v", expected, actual) @@ -167,7 +170,8 @@ func TestCreateFlavor(t *testing.T) { "ram": 512, "vcpus": 1, "rxtx_factor": 1, - "swap": "" + "swap": "", + "description": "foo" } } `) @@ -175,12 +179,13 @@ func TestCreateFlavor(t *testing.T) { disk := 1 opts := &flavors.CreateOpts{ - ID: "1", - Name: "m1.tiny", - Disk: &disk, - RAM: 512, - VCPUs: 1, - RxTxFactor: 1.0, + ID: "1", + Name: "m1.tiny", + Disk: &disk, + RAM: 512, + VCPUs: 1, + RxTxFactor: 1.0, + Description: "foo", } actual, err := flavors.Create(fake.ServiceClient(), opts).Extract() if err != nil { @@ -188,13 +193,14 @@ func TestCreateFlavor(t *testing.T) { } expected := &flavors.Flavor{ - ID: "1", - Name: "m1.tiny", - Disk: 1, - RAM: 512, - VCPUs: 1, - RxTxFactor: 1, - Swap: 0, + ID: "1", + Name: "m1.tiny", + Disk: 1, + RAM: 512, + VCPUs: 1, + RxTxFactor: 1, + Swap: 0, + Description: "foo", } if !reflect.DeepEqual(expected, actual) { t.Errorf("Expected %#v, but was %#v", expected, actual) From 6e8200121d50663e76dad5058748c15bdf3072c5 Mon Sep 17 00:00:00 2001 From: Pierre Prinetti <pierreprinetti@redhat.com> Date: Mon, 29 Aug 2022 14:32:10 +0200 Subject: [PATCH 126/360] Update changelog preparing v1.0.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Martin André <m.andre@redhat.com> --- CHANGELOG.md | 13 +++++++++++++ README.md | 4 +++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e5824a0216..0c8137c10d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,16 @@ +## 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 diff --git a/README.md b/README.md index b429f09051..c4fcf47533 100644 --- a/README.md +++ b/README.md @@ -126,7 +126,9 @@ Have a look at the [FAQ](./docs/FAQ.md) for some tips on customizing the way Gop ## Backwards-Compatibility Guarantees -None. Vendor it and write tests covering the parts you use. +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. ## Contributing From 804107e980e9d10dd1104df1ed42a46b3f9f21c6 Mon Sep 17 00:00:00 2001 From: Pierre Prinetti <pierreprinetti@redhat.com> Date: Thu, 1 Sep 2022 08:14:51 +0200 Subject: [PATCH 127/360] Fix typo in blockstorage/v3/attachments docs Fixes #2457 --- openstack/blockstorage/v3/attachments/doc.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openstack/blockstorage/v3/attachments/doc.go b/openstack/blockstorage/v3/attachments/doc.go index b3962d37f5..838d8962fc 100644 --- a/openstack/blockstorage/v3/attachments/doc.go +++ b/openstack/blockstorage/v3/attachments/doc.go @@ -29,7 +29,7 @@ Example to List Attachments Example to Create Attachment createOpts := &attachments.CreateOpts{ - InstanceiUUID: "uuid", + InstanceUUID: "uuid", VolumeUUID: "uuid" } From 5146c6c951de3c9019f1fa392dbc9f21a1b2fcc7 Mon Sep 17 00:00:00 2001 From: Pierre Prinetti <pierreprinetti@redhat.com> Date: Thu, 1 Sep 2022 09:25:03 +0200 Subject: [PATCH 128/360] actions: Add semver tools * Define three new labels: `semver:major`, `semver:minor`, `semver:patch` * Only allow merge if one of the `semver:` labels is present * Remove the `semver:` label upon new push to the pull request --- .github/semver-labels.yaml | 9 +++++++++ .github/workflows/semver-labels.yaml | 15 +++++++++++++++ .github/workflows/semver-require.yaml | 17 +++++++++++++++++ .github/workflows/semver-unlabel.yaml | 19 +++++++++++++++++++ 4 files changed, 60 insertions(+) create mode 100644 .github/semver-labels.yaml create mode 100644 .github/workflows/semver-labels.yaml create mode 100644 .github/workflows/semver-require.yaml create mode 100644 .github/workflows/semver-unlabel.yaml diff --git a/.github/semver-labels.yaml b/.github/semver-labels.yaml new file mode 100644 index 0000000000..f10205cc95 --- /dev/null +++ b/.github/semver-labels.yaml @@ -0,0 +1,9 @@ +- name: semver:major + description: Breaking change + color: '9E1957' +- name: semver:minor + description: Backwards-compatible change + color: 'D6FC76' +- name: semver:patch + description: No API change + color: 'DBF568' diff --git a/.github/workflows/semver-labels.yaml b/.github/workflows/semver-labels.yaml new file mode 100644 index 0000000000..3901a4458e --- /dev/null +++ b/.github/workflows/semver-labels.yaml @@ -0,0 +1,15 @@ +name: Semver labels +on: + push: + branches: + - master + paths: + - .github/semver-labels.yaml +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: micnncim/action-label-syncer@v1 + with: + manifest: .github/semver-labels.yaml diff --git a/.github/workflows/semver-require.yaml b/.github/workflows/semver-require.yaml new file mode 100644 index 0000000000..beac272b0e --- /dev/null +++ b/.github/workflows/semver-require.yaml @@ -0,0 +1,17 @@ +name: Pull Request Labels +on: + pull_request: + types: + - opened + - labeled + - unlabeled + - synchronize +jobs: + label: + runs-on: ubuntu-latest + steps: + - uses: mheap/github-action-required-labels@v2 + with: + mode: exactly + count: 1 + labels: "semver:patch, semver:minor, semver:major" diff --git a/.github/workflows/semver-unlabel.yaml b/.github/workflows/semver-unlabel.yaml new file mode 100644 index 0000000000..e0c835d559 --- /dev/null +++ b/.github/workflows/semver-unlabel.yaml @@ -0,0 +1,19 @@ +name: 'Reset semver labels on PR push' + +# **What it does**: When the content of a PR changes, this workflow removes the semver label +# **Why we have it**: To make sure semver labels are up-to-date. +# **Who does it impact**: Pull requests. + +on: + pull_request: + types: + - synchronize + +jobs: + remove-semver-label: + runs-on: ubuntu-latest + steps: + - name: Remove the semver label + uses: andymckay/labeler@1.0.4 + with: + remove-labels: "semver:patch, semver:minor, semver:major" From d777a1c3f7d4b139c43b1fc1dae87bce5f2ab78b Mon Sep 17 00:00:00 2001 From: nikParasyr <nik.parasyr@protonmail.com> Date: Tue, 30 Aug 2022 17:22:16 +0200 Subject: [PATCH 129/360] Add support for Update for flavors --- .../openstack/compute/v2/flavors_test.go | 24 +++++++++ openstack/compute/v2/flavors/doc.go | 13 +++++ openstack/compute/v2/flavors/requests.go | 31 ++++++++++++ openstack/compute/v2/flavors/results.go | 6 +++ .../v2/flavors/testing/requests_test.go | 49 +++++++++++++++++++ openstack/compute/v2/flavors/urls.go | 4 ++ 6 files changed, 127 insertions(+) diff --git a/acceptance/openstack/compute/v2/flavors_test.go b/acceptance/openstack/compute/v2/flavors_test.go index c58f99452e..9e3ec1db45 100644 --- a/acceptance/openstack/compute/v2/flavors_test.go +++ b/acceptance/openstack/compute/v2/flavors_test.go @@ -90,6 +90,30 @@ func TestFlavorsCreateDelete(t *testing.T) { tools.PrintResource(t, flavor) } +func TestFlavorsCreateUpdateDelete(t *testing.T) { + clients.RequireAdmin(t) + + client, err := clients.NewComputeV2Client() + th.AssertNoErr(t, err) + + flavor, err := CreateFlavor(t, client) + th.AssertNoErr(t, err) + defer DeleteFlavor(t, client, flavor) + + tools.PrintResource(t, flavor) + + newFlavorDescription := "This is the new description" + updateOpts := flavors.UpdateOpts{ + Description: newFlavorDescription, + } + + flavor, err = flavors.Update(client, flavor.ID, updateOpts).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, flavor.Description, newFlavorDescription) + + tools.PrintResource(t, flavor) +} + func TestFlavorsAccessesList(t *testing.T) { clients.RequireAdmin(t) diff --git a/openstack/compute/v2/flavors/doc.go b/openstack/compute/v2/flavors/doc.go index 34d8764fad..747966d8d9 100644 --- a/openstack/compute/v2/flavors/doc.go +++ b/openstack/compute/v2/flavors/doc.go @@ -42,6 +42,19 @@ Example to Create a Flavor panic(err) } +Example to Update a Flavor + + flavorID := "e91758d6-a54a-4778-ad72-0c73a1cb695b" + + updateOpts := flavors.UpdateOpts{ + Description: "This is a good description" + } + + flavor, err := flavors.Update(computeClient, flavorID, updateOpts).Extract() + if err != nil { + panic(err) + } + Example to List Flavor Access flavorID := "e91758d6-a54a-4778-ad72-0c73a1cb695b" diff --git a/openstack/compute/v2/flavors/requests.go b/openstack/compute/v2/flavors/requests.go index 61a8b7dc4a..3887cdfdca 100644 --- a/openstack/compute/v2/flavors/requests.go +++ b/openstack/compute/v2/flavors/requests.go @@ -154,6 +154,37 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create return } +type UpdateOptsBuilder interface { + ToFlavorUpdateMap() (map[string]interface{}, error) +} + +// UpdateOpts specifies parameters used for updating a flavor. +type UpdateOpts struct { + // Description is a free form description of the flavor. Limited to + // 65535 characters in length. Only printable characters are allowed. + // New in version 2.55 + Description string `json:"description,omitempty"` +} + +// ToFlavorUpdateMap constructs a request body from UpdateOpts. +func (opts UpdateOpts) ToFlavorUpdateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "flavor") +} + +// Update requests the update of a new flavor. +func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { + b, err := opts.ToFlavorUpdateMap() + 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 +} + // Get retrieves details of a single flavor. Use Extract to convert its // result into a Flavor. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { diff --git a/openstack/compute/v2/flavors/results.go b/openstack/compute/v2/flavors/results.go index 3cad7747be..0234402ed2 100644 --- a/openstack/compute/v2/flavors/results.go +++ b/openstack/compute/v2/flavors/results.go @@ -18,6 +18,12 @@ type CreateResult struct { commonResult } +// UpdateResult is the response of a Put operation. Call its Extract method to +// interpret it as a Flavor. +type UpdateResult struct { + commonResult +} + // GetResult is the response of a Get operations. Call its Extract method to // interpret it as a Flavor. type GetResult struct { diff --git a/openstack/compute/v2/flavors/testing/requests_test.go b/openstack/compute/v2/flavors/testing/requests_test.go index f045449aea..05b7fbb57a 100644 --- a/openstack/compute/v2/flavors/testing/requests_test.go +++ b/openstack/compute/v2/flavors/testing/requests_test.go @@ -207,6 +207,55 @@ func TestCreateFlavor(t *testing.T) { } } +func TestUpdateFlavor(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/flavors/12345678", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PUT") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + fmt.Fprintf(w, ` + { + "flavor": { + "id": "1", + "name": "m1.tiny", + "disk": 1, + "ram": 512, + "vcpus": 1, + "rxtx_factor": 1, + "swap": "", + "description": "foo" + } + } + `) + }) + + opts := &flavors.UpdateOpts{ + Description: "foo", + } + actual, err := flavors.Update(fake.ServiceClient(), "12345678", opts).Extract() + if err != nil { + t.Fatalf("Unable to update flavor: %v", err) + } + + expected := &flavors.Flavor{ + ID: "1", + Name: "m1.tiny", + Disk: 1, + RAM: 512, + VCPUs: 1, + RxTxFactor: 1, + Swap: 0, + Description: "foo", + } + + if !reflect.DeepEqual(expected, actual) { + t.Errorf("Expected %#v, but was %#v", expected, actual) + } +} + func TestDeleteFlavor(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() diff --git a/openstack/compute/v2/flavors/urls.go b/openstack/compute/v2/flavors/urls.go index 8620dd78ad..65bbb65401 100644 --- a/openstack/compute/v2/flavors/urls.go +++ b/openstack/compute/v2/flavors/urls.go @@ -16,6 +16,10 @@ func createURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("flavors") } +func updateURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL("flavors", id) +} + func deleteURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("flavors", id) } From 966e902b74b8fe3d7f0c4049ddd92ac568a7536d Mon Sep 17 00:00:00 2001 From: Pierre Prinetti <pierreprinetti@redhat.com> Date: Tue, 6 Sep 2022 11:33:04 +0200 Subject: [PATCH 130/360] Unlabeler: Fix permissions issue Run the action in the context of the base of the pull request, rather than in the context of the merge commit. This change also restricts the permissions to what is strictly needed for unlabeling. --- .github/workflows/semver-unlabel.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/semver-unlabel.yaml b/.github/workflows/semver-unlabel.yaml index e0c835d559..b949512370 100644 --- a/.github/workflows/semver-unlabel.yaml +++ b/.github/workflows/semver-unlabel.yaml @@ -5,12 +5,14 @@ name: 'Reset semver labels on PR push' # **Who does it impact**: Pull requests. on: - pull_request: + pull_request_target: types: - synchronize jobs: remove-semver-label: + permissions: + pull-requests: write runs-on: ubuntu-latest steps: - name: Remove the semver label From 4ae3a080dff820340fff94617b5e2aec6954305b Mon Sep 17 00:00:00 2001 From: emilmaruszczak <emil.maruszczak@thehutgroup.com> Date: Tue, 6 Sep 2022 19:56:08 +0200 Subject: [PATCH 131/360] Add GetEnforcementModel operation --- openstack/identity/v3/limits/doc.go | 7 +++++ openstack/identity/v3/limits/requests.go | 7 +++++ openstack/identity/v3/limits/results.go | 25 ++++++++++++++++ .../identity/v3/limits/testing/fixtures.go | 29 +++++++++++++++++++ .../v3/limits/testing/requests_test.go | 10 +++++++ openstack/identity/v3/limits/urls.go | 4 +++ 6 files changed, 82 insertions(+) diff --git a/openstack/identity/v3/limits/doc.go b/openstack/identity/v3/limits/doc.go index db2fc8a2ed..4f97669eff 100644 --- a/openstack/identity/v3/limits/doc.go +++ b/openstack/identity/v3/limits/doc.go @@ -2,6 +2,13 @@ Package limits provides information and interaction with limits for the Openstack Identity service. +Example to Get EnforcementModel + + model, err := limits.GetEnforcementModel(identityClient).Extract() + if err != nil { + panic(err) + } + Example to List Limits listOpts := limits.ListOpts{ diff --git a/openstack/identity/v3/limits/requests.go b/openstack/identity/v3/limits/requests.go index f9e78e7339..1bdac9dc30 100644 --- a/openstack/identity/v3/limits/requests.go +++ b/openstack/identity/v3/limits/requests.go @@ -5,6 +5,13 @@ import ( "github.com/gophercloud/gophercloud/pagination" ) +// Get retrieves details on a single limit, by ID. +func GetEnforcementModel(client *gophercloud.ServiceClient) (r EnforcementModelResult) { + resp, err := client.Get(enforcementModelURL(client), &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 { diff --git a/openstack/identity/v3/limits/results.go b/openstack/identity/v3/limits/results.go index ba57ad9dae..16ba63bc77 100644 --- a/openstack/identity/v3/limits/results.go +++ b/openstack/identity/v3/limits/results.go @@ -1,9 +1,34 @@ package limits import ( + "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) +// A model describing the configured enforcement model used by the deployment. +type EnforcementModel struct { + // The name of the enforcement model. + Name string `json:"name"` + + // A short description of the enforcement model used. + Description string `json:"description"` +} + +// EnforcementModelResult is the response from a GetEnforcementModel operation. Call its Extract method +// to interpret it as a EnforcementModel. +type EnforcementModelResult struct { + gophercloud.Result +} + +// Extract interprets EnforcementModelResult as a EnforcementModel. +func (r EnforcementModelResult) Extract() (*EnforcementModel, error) { + var out struct { + Model *EnforcementModel `json:"model"` + } + err := r.ExtractInto(&out) + return out.Model, err +} + // A limit is the limit that override the registered limit for each project. type Limit struct { // ID is the unique ID of the limit. diff --git a/openstack/identity/v3/limits/testing/fixtures.go b/openstack/identity/v3/limits/testing/fixtures.go index 69ab12ad45..ed502b3d01 100644 --- a/openstack/identity/v3/limits/testing/fixtures.go +++ b/openstack/identity/v3/limits/testing/fixtures.go @@ -10,6 +10,15 @@ import ( "github.com/gophercloud/gophercloud/testhelper/client" ) +const GetEnforcementModelOutput = ` +{ + "model": { + "description": "Limit enforcement and validation does not take project hierarchy into consideration.", + "name": "flat" + } +} +` + // ListOutput provides a single page of List results. const ListOutput = ` { @@ -49,6 +58,12 @@ const ListOutput = ` } ` +// Model is the enforcement model in the GetEnforcementModel request. +var Model = limits.EnforcementModel{ + Name: "flat", + Description: "Limit enforcement and validation does not take project hierarchy into consideration.", +} + // FirstLimit is the first limit in the List request. var FirstLimit = limits.Limit{ ResourceName: "volume", @@ -78,6 +93,20 @@ var SecondLimit = limits.Limit{ // ExpectedLimitsSlice is the slice of limits expected to be returned from ListOutput. var ExpectedLimitsSlice = []limits.Limit{FirstLimit, SecondLimit} +// HandleGetEnforcementModelSuccessfully creates an HTTP handler at `/limits/model` on the +// test handler mux that responds with a enforcement model. +func HandleGetEnforcementModelSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/limits/model", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "Accept", "application/json") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, GetEnforcementModelOutput) + }) +} + // HandleListLimitsSuccessfully creates an HTTP handler at `/limits` on the // test handler mux that responds with a list of two limits. func HandleListLimitsSuccessfully(t *testing.T) { diff --git a/openstack/identity/v3/limits/testing/requests_test.go b/openstack/identity/v3/limits/testing/requests_test.go index 42e55d691a..ec990357c3 100644 --- a/openstack/identity/v3/limits/testing/requests_test.go +++ b/openstack/identity/v3/limits/testing/requests_test.go @@ -9,6 +9,16 @@ import ( "github.com/gophercloud/gophercloud/testhelper/client" ) +func TestGetEnforcementModel(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleGetEnforcementModelSuccessfully(t) + + actual, err := limits.GetEnforcementModel(client.ServiceClient()).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, Model, *actual) +} + func TestListLimits(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() diff --git a/openstack/identity/v3/limits/urls.go b/openstack/identity/v3/limits/urls.go index 1892614541..022c8d9e52 100644 --- a/openstack/identity/v3/limits/urls.go +++ b/openstack/identity/v3/limits/urls.go @@ -2,6 +2,10 @@ package limits import "github.com/gophercloud/gophercloud" +func enforcementModelURL(client *gophercloud.ServiceClient) string { + return client.ServiceURL("limits", "model") +} + func listURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("limits") } From 56631052c71b0304a403e0e6c058de3fc528178a Mon Sep 17 00:00:00 2001 From: Pierre Prinetti <pierreprinetti@redhat.com> Date: Wed, 7 Sep 2022 16:34:41 +0200 Subject: [PATCH 132/360] tooling: Fix semver tooling issues --- .github/semver-labels.yaml | 4 ++-- .github/workflows/semver-labels.yaml | 7 +++++-- .github/workflows/semver-require.yaml | 4 ++-- .github/workflows/semver-unlabel.yaml | 8 ++++---- 4 files changed, 13 insertions(+), 10 deletions(-) diff --git a/.github/semver-labels.yaml b/.github/semver-labels.yaml index f10205cc95..7e9c8811b5 100644 --- a/.github/semver-labels.yaml +++ b/.github/semver-labels.yaml @@ -3,7 +3,7 @@ color: '9E1957' - name: semver:minor description: Backwards-compatible change - color: 'D6FC76' + color: 'FBCA04' - name: semver:patch description: No API change - color: 'DBF568' + color: '6E7624' diff --git a/.github/workflows/semver-labels.yaml b/.github/workflows/semver-labels.yaml index 3901a4458e..e6644a7bd7 100644 --- a/.github/workflows/semver-labels.yaml +++ b/.github/workflows/semver-labels.yaml @@ -1,15 +1,18 @@ -name: Semver labels +name: Ensure labels on: push: branches: - master paths: - .github/semver-labels.yaml + - .github/workflows/semver-labels.yaml jobs: - build: + semver: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: micnncim/action-label-syncer@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: manifest: .github/semver-labels.yaml diff --git a/.github/workflows/semver-require.yaml b/.github/workflows/semver-require.yaml index beac272b0e..58cc23b54c 100644 --- a/.github/workflows/semver-require.yaml +++ b/.github/workflows/semver-require.yaml @@ -1,4 +1,4 @@ -name: Pull Request Labels +name: Verify PR Labels on: pull_request: types: @@ -7,7 +7,7 @@ on: - unlabeled - synchronize jobs: - label: + semver: runs-on: ubuntu-latest steps: - uses: mheap/github-action-required-labels@v2 diff --git a/.github/workflows/semver-unlabel.yaml b/.github/workflows/semver-unlabel.yaml index b949512370..9fdf5558e4 100644 --- a/.github/workflows/semver-unlabel.yaml +++ b/.github/workflows/semver-unlabel.yaml @@ -1,4 +1,4 @@ -name: 'Reset semver labels on PR push' +name: Reset PR labels on push # **What it does**: When the content of a PR changes, this workflow removes the semver label # **Why we have it**: To make sure semver labels are up-to-date. @@ -10,12 +10,12 @@ on: - synchronize jobs: - remove-semver-label: - permissions: - pull-requests: write + semver: runs-on: ubuntu-latest steps: - name: Remove the semver label uses: andymckay/labeler@1.0.4 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: remove-labels: "semver:patch, semver:minor, semver:major" From 3ed00e1164f8fbe10eb9e85700afca5994d9ef90 Mon Sep 17 00:00:00 2001 From: shhgs <shhgs@yahoo.com> Date: Wed, 1 Jun 2022 10:14:24 -0400 Subject: [PATCH 133/360] Neutron v2: ScheduleBGPSpeakerOpts, RemoveBGPSpeaker, ListDRAgentHostingBGPSpeakers --- .../v2/extensions/agents/agents_test.go | 53 +++++++++++ .../networking/v2/extensions/agents/doc.go | 29 ++++++ .../v2/extensions/agents/requests.go | 47 ++++++++++ .../v2/extensions/agents/results.go | 14 +++ .../v2/extensions/agents/testing/fixtures.go | 90 +++++++++++++++++++ .../agents/testing/requests_test.go | 82 +++++++++++++++++ .../networking/v2/extensions/agents/urls.go | 15 ++++ 7 files changed, 330 insertions(+) diff --git a/acceptance/openstack/networking/v2/extensions/agents/agents_test.go b/acceptance/openstack/networking/v2/extensions/agents/agents_test.go index 3be89554b8..6ffe53b1c1 100644 --- a/acceptance/openstack/networking/v2/extensions/agents/agents_test.go +++ b/acceptance/openstack/networking/v2/extensions/agents/agents_test.go @@ -8,8 +8,10 @@ import ( "github.com/gophercloud/gophercloud/acceptance/clients" networking "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2" + spk "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2/extensions/bgp/speakers" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/agents" + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/bgp/speakers" th "github.com/gophercloud/gophercloud/testhelper" ) @@ -92,3 +94,54 @@ func TestAgentsRUD(t *testing.T) { err = agents.Delete(client, allAgents[0].ID).ExtractErr() th.AssertNoErr(t, err) } + +func TestBGPAgentRUD(t *testing.T) { + clients.RequireAdmin(t) + + client, err := clients.NewNetworkV2Client() + th.AssertNoErr(t, err) + + // List BGP Agents + listOpts := &agents.ListOpts{ + AgentType: "BGP Dynamic Routing Agent", + } + allPages, err := agents.List(client, listOpts).AllPages() + th.AssertNoErr(t, err) + + allAgents, err := agents.ExtractAgents(allPages) + th.AssertNoErr(t, err) + + t.Logf("Retrieved BGP agents") + tools.PrintResource(t, allAgents) + + // Create a BGP Speaker + bgpSpeaker, err := spk.CreateBGPSpeaker(t, client) + th.AssertNoErr(t, err) + + // List the BGP Agent that accommodate the BGP Speaker + pages, err := agents.ListDRAgentHostingBGPSpeakers(client, bgpSpeaker.ID).AllPages() + th.AssertNoErr(t, err) + bgpAgents, err := agents.ExtractAgents(pages) + th.AssertNoErr(t, err) + th.AssertEquals(t, len(bgpAgents), 1) + bgpAgent := bgpAgents[0] + t.Logf("BGP Speaker %s has been scheudled to agent %s", bgpSpeaker.ID, bgpAgent.ID) + + // Remove the BGP speaker from the agent + err = agents.RemoveBGPSpeaker(client, bgpAgent.ID, bgpSpeaker.ID).ExtractErr() + th.AssertNoErr(t, err) + t.Logf("Successfully removed speaker %s from agent %s", bgpSpeaker.ID, bgpAgent.ID) + + // Schedule a BGP Speaker to an agent + opts := agents.ScheduleBGPSpeakerOpts{ + SpeakerID: bgpSpeaker.ID, + } + err = agents.ScheduleBGPSpeaker(client, bgpAgent.ID, opts).ExtractErr() + th.AssertNoErr(t, err) + t.Logf("Successfully scheduled speaker %s to agent %s", bgpSpeaker.ID, bgpAgent.ID) + + // Delete the BGP Speaker + speakers.Delete(client, bgpSpeaker.ID).ExtractErr() + th.AssertNoErr(t, err) + t.Logf("Successfully deleted the BGP Speaker, %s", bgpSpeaker.ID) +} diff --git a/openstack/networking/v2/extensions/agents/doc.go b/openstack/networking/v2/extensions/agents/doc.go index e20b58c797..59eb233bd4 100644 --- a/openstack/networking/v2/extensions/agents/doc.go +++ b/openstack/networking/v2/extensions/agents/doc.go @@ -97,6 +97,35 @@ Example to List BGP speakers by dragent log.Printf("%v", s) } +Example to Schedule bgp speaker to dragent + + var opts agents.ScheduleBGPSpeakerOpts + opts.SpeakerID = speakerID + err := agents.ScheduleBGPSpeaker(c, agentID, opts).ExtractErr() + if err != nil { + log.Panic(err) + } + +Example to Remove bgp speaker from dragent + + err := agents.RemoveBGPSpeaker(c, agentID, speakerID).ExtractErr() + if err != nil { + log.Panic(err) + } + +Example to list dragents hosting specific bgp speaker + + pages, err := agents.ListDRAgentHostingBGPSpeakers(client, speakerID).AllPages() + if err != nil { + log.Panic(err) + } + allAgents, err := agents.ExtractAgents(pages) + if err != nil { + log.Panic(err) + } + for _, a := range allAgents { + log.Printf("%+v", a) + } */ package agents diff --git a/openstack/networking/v2/extensions/agents/requests.go b/openstack/networking/v2/extensions/agents/requests.go index 0aff95af0e..1f0729332f 100644 --- a/openstack/networking/v2/extensions/agents/requests.go +++ b/openstack/networking/v2/extensions/agents/requests.go @@ -158,3 +158,50 @@ func ListBGPSpeakers(c *gophercloud.ServiceClient, agentID string) pagination.Pa return ListBGPSpeakersResult{pagination.SinglePageBase(r)} }) } + +// ScheduleBGPSpeakerOptsBuilder declare a function that build ScheduleBGPSpeakerOpts into a request body +type ScheduleBGPSpeakerOptsBuilder interface { + ToAgentScheduleBGPSpeakerMap() (map[string]interface{}, error) +} + +// ScheduleBGPSpeakerOpts represents the data that would be POST to the endpoint +type ScheduleBGPSpeakerOpts struct { + SpeakerID string `json:"bgp_speaker_id" required:"true"` +} + +// ToAgentScheduleBGPSpeakerMap builds a request body from ScheduleBGPSpeakerOpts +func (opts ScheduleBGPSpeakerOpts) ToAgentScheduleBGPSpeakerMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "") +} + +// ScheduleBGPSpeaker schedule a BGP speaker to a BGP agent +// POST /v2.0/agents/{agent-id}/bgp-drinstances +func ScheduleBGPSpeaker(c *gophercloud.ServiceClient, agentID string, opts ScheduleBGPSpeakerOptsBuilder) (r ScheduleBGPSpeakerResult) { + b, err := opts.ToAgentScheduleBGPSpeakerMap() + if err != nil { + r.Err = err + return + } + resp, err := c.Post(scheduleBGPSpeakersURL(c, agentID), b, nil, &gophercloud.RequestOpts{ + OkCodes: []int{201}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// RemoveBGPSpeaker removoes a BGP speaker from a BGP agent +// DELETE /v2.0/agents/{agent-id}/bgp-drinstances +func RemoveBGPSpeaker(c *gophercloud.ServiceClient, agentID string, speakerID string) (r RemoveBGPSpeakerResult) { + resp, err := c.Delete(removeBGPSpeakersURL(c, agentID, speakerID), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// ListDRAgentHostingBGPSpeakers the dragents that are hosting a specific bgp speaker +// GET /v2.0/bgp-speakers/{bgp-speaker-id}/bgp-dragents +func ListDRAgentHostingBGPSpeakers(c *gophercloud.ServiceClient, bgpSpeakerID string) pagination.Pager { + url := listDRAgentHostingBGPSpeakersURL(c, bgpSpeakerID) + return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { + return AgentPage{pagination.LinkedPageBase{PageResult: r}} + }) +} diff --git a/openstack/networking/v2/extensions/agents/results.go b/openstack/networking/v2/extensions/agents/results.go index 4e84da2ef6..5f66eb37fe 100644 --- a/openstack/networking/v2/extensions/agents/results.go +++ b/openstack/networking/v2/extensions/agents/results.go @@ -55,6 +55,20 @@ type RemoveDHCPNetworkResult struct { gophercloud.ErrResult } +// ScheduleBGPSpeakerResult represents the result of adding a BGP speaker to a +// BGP DR Agent. ExtractErr method to determine if the request succeeded or +// failed. +type ScheduleBGPSpeakerResult struct { + gophercloud.ErrResult +} + +// RemoveBGPSpeakerResult represents the result of removing a BGP speaker from a +// BGP DR Agent. ExtractErr method to determine if the request succeeded or +// failed. +type RemoveBGPSpeakerResult struct { + gophercloud.ErrResult +} + // Agent represents a Neutron agent. type Agent struct { // ID is the id of the agent. diff --git a/openstack/networking/v2/extensions/agents/testing/fixtures.go b/openstack/networking/v2/extensions/agents/testing/fixtures.go index 9ab0c91a62..35b4ca5232 100644 --- a/openstack/networking/v2/extensions/agents/testing/fixtures.go +++ b/openstack/networking/v2/extensions/agents/testing/fixtures.go @@ -255,3 +255,93 @@ const ListBGPSpeakersResult = ` ] } ` +const ScheduleBGPSpeakerRequest = ` +{ + "bgp_speaker_id": "8edb2c68-0654-49a9-b3fe-030f92e3ddf6" +} +` + +var BGPAgent1 = agents.Agent{ + ID: "60d78b78-b56b-4d91-a174-2c03159f6bb6", + AdminStateUp: true, + AgentType: "BGP dynamic routing agent", + Alive: true, + Binary: "neutron-bgp-dragent", + Configurations: map[string]interface{}{ + "advertise_routes": float64(2), + "bgp_peers": float64(2), + "bgp_speakers": float64(1), + }, + CreatedAt: time.Date(2020, 9, 17, 20, 8, 58, 0, time.UTC), + StartedAt: time.Date(2021, 5, 4, 11, 13, 12, 0, time.UTC), + HeartbeatTimestamp: time.Date(2021, 9, 13, 19, 55, 1, 0, time.UTC), + Host: "agent1.example.com", + Topic: "bgp_dragent", +} + +var BGPAgent2 = agents.Agent{ + ID: "d0bdcea2-1d02-4c1d-9e79-b827e77acc22", + AdminStateUp: true, + AgentType: "BGP dynamic routing agent", + Alive: true, + Binary: "neutron-bgp-dragent", + Configurations: map[string]interface{}{ + "advertise_routes": float64(2), + "bgp_peers": float64(2), + "bgp_speakers": float64(1), + }, + CreatedAt: time.Date(2020, 9, 17, 20, 8, 15, 0, time.UTC), + StartedAt: time.Date(2021, 5, 4, 11, 13, 13, 0, time.UTC), + HeartbeatTimestamp: time.Date(2021, 9, 13, 19, 54, 47, 0, time.UTC), + Host: "agent2.example.com", + Topic: "bgp_dragent", +} + +const ListDRAgentHostingBGPSpeakersResult = ` +{ + "agents": [ + { + "binary": "neutron-bgp-dragent", + "description": null, + "availability_zone": null, + "heartbeat_timestamp": "2021-09-13 19:55:01", + "admin_state_up": true, + "resources_synced": null, + "alive": true, + "topic": "bgp_dragent", + "host": "agent1.example.com", + "agent_type": "BGP dynamic routing agent", + "resource_versions": {}, + "created_at": "2020-09-17 20:08:58", + "started_at": "2021-05-04 11:13:12", + "id": "60d78b78-b56b-4d91-a174-2c03159f6bb6", + "configurations": { + "advertise_routes": 2, + "bgp_peers": 2, + "bgp_speakers": 1 + } + }, + { + "binary": "neutron-bgp-dragent", + "description": null, + "availability_zone": null, + "heartbeat_timestamp": "2021-09-13 19:54:47", + "admin_state_up": true, + "resources_synced": null, + "alive": true, + "topic": "bgp_dragent", + "host": "agent2.example.com", + "agent_type": "BGP dynamic routing agent", + "resource_versions": {}, + "created_at": "2020-09-17 20:08:15", + "started_at": "2021-05-04 11:13:13", + "id": "d0bdcea2-1d02-4c1d-9e79-b827e77acc22", + "configurations": { + "advertise_routes": 2, + "bgp_peers": 2, + "bgp_speakers": 1 + } + } + ] +} +` diff --git a/openstack/networking/v2/extensions/agents/testing/requests_test.go b/openstack/networking/v2/extensions/agents/testing/requests_test.go index 323e750c4a..10411b5910 100644 --- a/openstack/networking/v2/extensions/agents/testing/requests_test.go +++ b/openstack/networking/v2/extensions/agents/testing/requests_test.go @@ -241,3 +241,85 @@ func TestListBGPSpeakers(t *testing.T) { t.Errorf("Expected 1 page, got %d", count) } } + +func TestScheduleBGPSpeaker(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + agentID := "30d76012-46de-4215-aaa1-a1630d01d891" + speakerID := "8edb2c68-0654-49a9-b3fe-030f92e3ddf6" + + th.Mux.HandleFunc("/v2.0/agents/"+agentID+"/bgp-drinstances", + func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestHeader(t, r, "Content-Type", "application/json") + th.TestHeader(t, r, "Accept", "application/json") + th.TestJSONRequest(t, r, ScheduleBGPSpeakerRequest) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusCreated) + }) + + var opts agents.ScheduleBGPSpeakerOpts + opts.SpeakerID = speakerID + err := agents.ScheduleBGPSpeaker(fake.ServiceClient(), agentID, opts).ExtractErr() + th.AssertNoErr(t, err) +} + +func TestRemoveBGPSpeaker(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + agentID := "30d76012-46de-4215-aaa1-a1630d01d891" + speakerID := "8edb2c68-0654-49a9-b3fe-030f92e3ddf6" + + th.Mux.HandleFunc("/v2.0/agents/"+agentID+"/bgp-drinstances/"+speakerID, + func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "DELETE") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestHeader(t, r, "Accept", "application/json") + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusNoContent) + }) + + err := agents.RemoveBGPSpeaker(fake.ServiceClient(), agentID, speakerID).ExtractErr() + th.AssertNoErr(t, err) +} + +func TestListDRAgentHostingBGPSpeakers(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + speakerID := "3f511b1b-d541-45f1-aa98-2e44e8183d4c" + th.Mux.HandleFunc("/v2.0/bgp-speakers/"+speakerID+"/bgp-dragents", + func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, ListDRAgentHostingBGPSpeakersResult) + }) + + count := 0 + agents.ListDRAgentHostingBGPSpeakers(fake.ServiceClient(), speakerID).EachPage( + func(page pagination.Page) (bool, error) { + count++ + actual, err := agents.ExtractAgents(page) + + if err != nil { + t.Errorf("Failed to extract agents: %v", err) + return false, nil + } + + expected := []agents.Agent{BGPAgent1, BGPAgent2} + th.CheckDeepEquals(t, expected, actual) + return true, nil + }) + + if count != 1 { + t.Errorf("Expected 1 page, got %d", count) + } +} diff --git a/openstack/networking/v2/extensions/agents/urls.go b/openstack/networking/v2/extensions/agents/urls.go index e357d0a08c..237760c510 100644 --- a/openstack/networking/v2/extensions/agents/urls.go +++ b/openstack/networking/v2/extensions/agents/urls.go @@ -50,3 +50,18 @@ func removeDHCPNetworkURL(c *gophercloud.ServiceClient, id string, networkID str func listBGPSpeakersURL(c *gophercloud.ServiceClient, agentID string) string { return c.ServiceURL(resourcePath, agentID, bgpSpeakersResourcePath) } + +// return /v2.0/agents/{agent-id}/bgp-drinstances +func scheduleBGPSpeakersURL(c *gophercloud.ServiceClient, id string) string { + return listBGPSpeakersURL(c, id) +} + +// return /v2.0/agents/{agent-id}/bgp-drinstances/{bgp-speaker-id} +func removeBGPSpeakersURL(c *gophercloud.ServiceClient, agentID string, speakerID string) string { + return c.ServiceURL(resourcePath, agentID, bgpSpeakersResourcePath, speakerID) +} + +// return /v2.0/bgp-speakers/{bgp-speaker-id}/bgp-dragents +func listDRAgentHostingBGPSpeakersURL(c *gophercloud.ServiceClient, speakerID string) string { + return c.ServiceURL("bgp-speakers", speakerID, "bgp-dragents") +} From a90725c57f7077bc29d8d6190fb082a301dcdf9a Mon Sep 17 00:00:00 2001 From: shhgs <shhgs@yahoo.com> Date: Wed, 7 Sep 2022 16:30:40 -0400 Subject: [PATCH 134/360] Use agent.Configurations to assert BGP speaker scheduling --- .../v2/extensions/agents/agents_test.go | 45 ++++++++++++++++--- 1 file changed, 38 insertions(+), 7 deletions(-) diff --git a/acceptance/openstack/networking/v2/extensions/agents/agents_test.go b/acceptance/openstack/networking/v2/extensions/agents/agents_test.go index 6ffe53b1c1..f0881cedc2 100644 --- a/acceptance/openstack/networking/v2/extensions/agents/agents_test.go +++ b/acceptance/openstack/networking/v2/extensions/agents/agents_test.go @@ -5,6 +5,7 @@ package agents import ( "testing" + "time" "github.com/gophercloud/gophercloud/acceptance/clients" networking "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2" @@ -96,6 +97,7 @@ func TestAgentsRUD(t *testing.T) { } func TestBGPAgentRUD(t *testing.T) { + waitTime := 30 * time.Second clients.RequireAdmin(t) client, err := clients.NewNetworkV2Client() @@ -117,21 +119,40 @@ func TestBGPAgentRUD(t *testing.T) { // Create a BGP Speaker bgpSpeaker, err := spk.CreateBGPSpeaker(t, client) th.AssertNoErr(t, err) + time.Sleep(waitTime) - // List the BGP Agent that accommodate the BGP Speaker + // List the BGP Agents that accommodate the BGP Speaker pages, err := agents.ListDRAgentHostingBGPSpeakers(client, bgpSpeaker.ID).AllPages() th.AssertNoErr(t, err) bgpAgents, err := agents.ExtractAgents(pages) th.AssertNoErr(t, err) - th.AssertEquals(t, len(bgpAgents), 1) - bgpAgent := bgpAgents[0] - t.Logf("BGP Speaker %s has been scheudled to agent %s", bgpSpeaker.ID, bgpAgent.ID) + th.AssertIntGreaterOrEqual(t, len(bgpAgents), 1) + for _, agt := range bgpAgents { + t.Logf("BGP Speaker %s has been scheduled to agent %s", bgpSpeaker.ID, agt.ID) + bgpAgent, err := agents.Get(client, agt.ID).Extract() + th.AssertNoErr(t, err) + numOfSpeakers := int(bgpAgent.Configurations["bgp_speakers"].(float64)) + th.AssertEquals(t, numOfSpeakers, 1) + } - // Remove the BGP speaker from the agent - err = agents.RemoveBGPSpeaker(client, bgpAgent.ID, bgpSpeaker.ID).ExtractErr() + // Remove the BGP Speaker from the first agent + err = agents.RemoveBGPSpeaker(client, bgpAgents[0].ID, bgpSpeaker.ID).ExtractErr() th.AssertNoErr(t, err) - t.Logf("Successfully removed speaker %s from agent %s", bgpSpeaker.ID, bgpAgent.ID) + t.Logf("BGP Speaker %s has been removed from agent %s", bgpSpeaker.ID, bgpAgents[0].ID) + time.Sleep(waitTime) + bgpAgent, err := agents.Get(client, bgpAgents[0].ID).Extract() + th.AssertNoErr(t, err) + agentConf := bgpAgent.Configurations + th.AssertEquals(t, int(agentConf["bgp_speakers"].(float64)), 0) + // Remove all BGP Speakers from the agent + pages, err = agents.ListBGPSpeakers(client, bgpAgent.ID).AllPages() + th.AssertNoErr(t, err) + allSpeakers, err := agents.ExtractBGPSpeakers(pages) + th.AssertNoErr(t, err) + for _, speaker := range allSpeakers { + th.AssertNoErr(t, agents.RemoveBGPSpeaker(client, bgpAgent.ID, speaker.ID).ExtractErr()) + } // Schedule a BGP Speaker to an agent opts := agents.ScheduleBGPSpeakerOpts{ SpeakerID: bgpSpeaker.ID, @@ -139,9 +160,19 @@ func TestBGPAgentRUD(t *testing.T) { err = agents.ScheduleBGPSpeaker(client, bgpAgent.ID, opts).ExtractErr() th.AssertNoErr(t, err) t.Logf("Successfully scheduled speaker %s to agent %s", bgpSpeaker.ID, bgpAgent.ID) + time.Sleep(waitTime) + bgpAgent, err = agents.Get(client, bgpAgent.ID).Extract() + th.AssertNoErr(t, err) + agentConf = bgpAgent.Configurations + th.AssertEquals(t, 1, int(agentConf["bgp_speakers"].(float64))) // Delete the BGP Speaker speakers.Delete(client, bgpSpeaker.ID).ExtractErr() th.AssertNoErr(t, err) t.Logf("Successfully deleted the BGP Speaker, %s", bgpSpeaker.ID) + time.Sleep(waitTime) + bgpAgent, err = agents.Get(client, bgpAgent.ID).Extract() + th.AssertNoErr(t, err) + agentConf = bgpAgent.Configurations + th.AssertEquals(t, 0, int(agentConf["bgp_speakers"].(float64))) } From b0f867aed22bc2be7836479dc2ba0b82fe095e2b Mon Sep 17 00:00:00 2001 From: shhgs <shhgs@yahoo.com> Date: Thu, 8 Sep 2022 09:03:32 -0400 Subject: [PATCH 135/360] Make literals constant var in the URL generation --- openstack/networking/v2/extensions/agents/requests.go | 2 +- openstack/networking/v2/extensions/agents/urls.go | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/openstack/networking/v2/extensions/agents/requests.go b/openstack/networking/v2/extensions/agents/requests.go index 1f0729332f..707f710c4e 100644 --- a/openstack/networking/v2/extensions/agents/requests.go +++ b/openstack/networking/v2/extensions/agents/requests.go @@ -189,7 +189,7 @@ func ScheduleBGPSpeaker(c *gophercloud.ServiceClient, agentID string, opts Sched return } -// RemoveBGPSpeaker removoes a BGP speaker from a BGP agent +// RemoveBGPSpeaker removes a BGP speaker from a BGP agent // DELETE /v2.0/agents/{agent-id}/bgp-drinstances func RemoveBGPSpeaker(c *gophercloud.ServiceClient, agentID string, speakerID string) (r RemoveBGPSpeakerResult) { resp, err := c.Delete(removeBGPSpeakersURL(c, agentID, speakerID), nil) diff --git a/openstack/networking/v2/extensions/agents/urls.go b/openstack/networking/v2/extensions/agents/urls.go index 237760c510..f6021fce2d 100644 --- a/openstack/networking/v2/extensions/agents/urls.go +++ b/openstack/networking/v2/extensions/agents/urls.go @@ -5,6 +5,8 @@ import "github.com/gophercloud/gophercloud" const resourcePath = "agents" const dhcpNetworksResourcePath = "dhcp-networks" const bgpSpeakersResourcePath = "bgp-drinstances" +const bgpDRAgentSpeakersResourcePath = "bgp-speakers" +const bgpDRAgentAgentResourcePath = "bgp-dragents" func resourceURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL(resourcePath, id) @@ -63,5 +65,5 @@ func removeBGPSpeakersURL(c *gophercloud.ServiceClient, agentID string, speakerI // return /v2.0/bgp-speakers/{bgp-speaker-id}/bgp-dragents func listDRAgentHostingBGPSpeakersURL(c *gophercloud.ServiceClient, speakerID string) string { - return c.ServiceURL("bgp-speakers", speakerID, "bgp-dragents") + return c.ServiceURL(bgpDRAgentSpeakersResourcePath, speakerID, bgpDRAgentAgentResourcePath) } From 37211121f156ced1619c5326148412e5f9990bbf Mon Sep 17 00:00:00 2001 From: shhgs <shhgs@yahoo.com> Date: Thu, 8 Sep 2022 11:22:01 -0400 Subject: [PATCH 136/360] Use tools.WaitForTimeout to poll the state --- .../v2/extensions/agents/agents_test.go | 76 ++++++++++++------- 1 file changed, 49 insertions(+), 27 deletions(-) diff --git a/acceptance/openstack/networking/v2/extensions/agents/agents_test.go b/acceptance/openstack/networking/v2/extensions/agents/agents_test.go index f0881cedc2..db34e75a1c 100644 --- a/acceptance/openstack/networking/v2/extensions/agents/agents_test.go +++ b/acceptance/openstack/networking/v2/extensions/agents/agents_test.go @@ -97,7 +97,7 @@ func TestAgentsRUD(t *testing.T) { } func TestBGPAgentRUD(t *testing.T) { - waitTime := 30 * time.Second + timeout := 120 * time.Second clients.RequireAdmin(t) client, err := clients.NewNetworkV2Client() @@ -119,60 +119,82 @@ func TestBGPAgentRUD(t *testing.T) { // Create a BGP Speaker bgpSpeaker, err := spk.CreateBGPSpeaker(t, client) th.AssertNoErr(t, err) - time.Sleep(waitTime) - - // List the BGP Agents that accommodate the BGP Speaker pages, err := agents.ListDRAgentHostingBGPSpeakers(client, bgpSpeaker.ID).AllPages() th.AssertNoErr(t, err) bgpAgents, err := agents.ExtractAgents(pages) th.AssertNoErr(t, err) th.AssertIntGreaterOrEqual(t, len(bgpAgents), 1) - for _, agt := range bgpAgents { - t.Logf("BGP Speaker %s has been scheduled to agent %s", bgpSpeaker.ID, agt.ID) - bgpAgent, err := agents.Get(client, agt.ID).Extract() - th.AssertNoErr(t, err) - numOfSpeakers := int(bgpAgent.Configurations["bgp_speakers"].(float64)) - th.AssertEquals(t, numOfSpeakers, 1) - } + + // List the BGP Agents that accommodate the BGP Speaker + err = tools.WaitForTimeout( + func() (bool, error) { + flag := true + for _, agt := range bgpAgents { + t.Logf("BGP Speaker %s has been scheduled to agent %s", bgpSpeaker.ID, agt.ID) + bgpAgent, err := agents.Get(client, agt.ID).Extract() + th.AssertNoErr(t, err) + numOfSpeakers := int(bgpAgent.Configurations["bgp_speakers"].(float64)) + flag = flag && (numOfSpeakers == 1) + } + return flag, nil + }, timeout) + th.AssertNoErr(t, err) // Remove the BGP Speaker from the first agent err = agents.RemoveBGPSpeaker(client, bgpAgents[0].ID, bgpSpeaker.ID).ExtractErr() th.AssertNoErr(t, err) t.Logf("BGP Speaker %s has been removed from agent %s", bgpSpeaker.ID, bgpAgents[0].ID) - time.Sleep(waitTime) - bgpAgent, err := agents.Get(client, bgpAgents[0].ID).Extract() + err = tools.WaitForTimeout( + func() (bool, error) { + bgpAgent, err := agents.Get(client, bgpAgents[0].ID).Extract() + th.AssertNoErr(t, err) + agentConf := bgpAgent.Configurations + numOfSpeakers := int(agentConf["bgp_speakers"].(float64)) + t.Logf("Agent %s has %d speakers", bgpAgent.ID, numOfSpeakers) + return numOfSpeakers == 0, nil + }, timeout) th.AssertNoErr(t, err) - agentConf := bgpAgent.Configurations - th.AssertEquals(t, int(agentConf["bgp_speakers"].(float64)), 0) // Remove all BGP Speakers from the agent - pages, err = agents.ListBGPSpeakers(client, bgpAgent.ID).AllPages() + pages, err = agents.ListBGPSpeakers(client, bgpAgents[0].ID).AllPages() th.AssertNoErr(t, err) allSpeakers, err := agents.ExtractBGPSpeakers(pages) th.AssertNoErr(t, err) for _, speaker := range allSpeakers { - th.AssertNoErr(t, agents.RemoveBGPSpeaker(client, bgpAgent.ID, speaker.ID).ExtractErr()) + th.AssertNoErr(t, agents.RemoveBGPSpeaker(client, bgpAgents[0].ID, speaker.ID).ExtractErr()) } + // Schedule a BGP Speaker to an agent opts := agents.ScheduleBGPSpeakerOpts{ SpeakerID: bgpSpeaker.ID, } - err = agents.ScheduleBGPSpeaker(client, bgpAgent.ID, opts).ExtractErr() + err = agents.ScheduleBGPSpeaker(client, bgpAgents[0].ID, opts).ExtractErr() th.AssertNoErr(t, err) - t.Logf("Successfully scheduled speaker %s to agent %s", bgpSpeaker.ID, bgpAgent.ID) - time.Sleep(waitTime) - bgpAgent, err = agents.Get(client, bgpAgent.ID).Extract() + t.Logf("Successfully scheduled speaker %s to agent %s", bgpSpeaker.ID, bgpAgents[0].ID) + + err = tools.WaitForTimeout( + func() (bool, error) { + bgpAgent, err := agents.Get(client, bgpAgents[0].ID).Extract() + th.AssertNoErr(t, err) + agentConf := bgpAgent.Configurations + numOfSpeakers := int(agentConf["bgp_speakers"].(float64)) + t.Logf("Agent %s has %d speakers", bgpAgent.ID, numOfSpeakers) + return 1 == numOfSpeakers, nil + }, timeout) th.AssertNoErr(t, err) - agentConf = bgpAgent.Configurations - th.AssertEquals(t, 1, int(agentConf["bgp_speakers"].(float64))) // Delete the BGP Speaker speakers.Delete(client, bgpSpeaker.ID).ExtractErr() th.AssertNoErr(t, err) t.Logf("Successfully deleted the BGP Speaker, %s", bgpSpeaker.ID) - time.Sleep(waitTime) - bgpAgent, err = agents.Get(client, bgpAgent.ID).Extract() + err = tools.WaitForTimeout( + func() (bool, error) { + bgpAgent, err := agents.Get(client, bgpAgents[0].ID).Extract() + th.AssertNoErr(t, err) + agentConf := bgpAgent.Configurations + numOfSpeakers := int(agentConf["bgp_speakers"].(float64)) + t.Logf("Agent %s has %d speakers", bgpAgent.ID, numOfSpeakers) + return 0 == numOfSpeakers, nil + }, timeout) th.AssertNoErr(t, err) - agentConf = bgpAgent.Configurations - th.AssertEquals(t, 0, int(agentConf["bgp_speakers"].(float64))) } From 48581dacb7ec726e2ea5ed5094e0bf91db20751b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= <m.andre@redhat.com> Date: Mon, 12 Sep 2022 09:28:04 +0200 Subject: [PATCH 137/360] Support old time format for port CreatedAt and UpdatedAt Older versions of neutron returned times as RFC 3339 "no Z" format. While gophercloud currently supports OpenStack versions from Train and above, all returning the new RFC 3339 time format, there's no reasons to be hostile against older OpenStack releases. This commit makes the Port.CreatedAt and Port.UpdatedAt fields compatible with older OpenStack releases. Fixes #2469 --- .../extensions/dns/testing/requests_test.go | 2 + .../portsbinding/testing/requests_test.go | 3 ++ openstack/networking/v2/ports/results.go | 43 ++++++++++++++++++- .../networking/v2/ports/testing/fixtures.go | 8 +++- .../v2/ports/testing/requests_test.go | 9 +++- 5 files changed, 60 insertions(+), 5 deletions(-) diff --git a/openstack/networking/v2/extensions/dns/testing/requests_test.go b/openstack/networking/v2/extensions/dns/testing/requests_test.go index 3a792e97b2..931022c3ec 100644 --- a/openstack/networking/v2/extensions/dns/testing/requests_test.go +++ b/openstack/networking/v2/extensions/dns/testing/requests_test.go @@ -57,6 +57,8 @@ func TestPortList(t *testing.T) { ID: "d80b1a3b-4fc1-49f3-952e-1e2ab7081d8b", SecurityGroups: []string{}, DeviceID: "9ae135f4-b6e0-4dad-9e91-3c223e385824", + CreatedAt: time.Date(2019, time.June, 30, 4, 15, 37, 0, time.UTC), + UpdatedAt: time.Date(2019, time.June, 30, 5, 18, 49, 0, time.UTC), }, PortDNSExt: dns.PortDNSExt{ DNSName: "test-port", diff --git a/openstack/networking/v2/extensions/portsbinding/testing/requests_test.go b/openstack/networking/v2/extensions/portsbinding/testing/requests_test.go index 8f3da66743..666f406ca4 100644 --- a/openstack/networking/v2/extensions/portsbinding/testing/requests_test.go +++ b/openstack/networking/v2/extensions/portsbinding/testing/requests_test.go @@ -2,6 +2,7 @@ package testing import ( "testing" + "time" fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/portsbinding" @@ -40,6 +41,8 @@ func TestList(t *testing.T) { ID: "d80b1a3b-4fc1-49f3-952e-1e2ab7081d8b", SecurityGroups: []string{}, DeviceID: "9ae135f4-b6e0-4dad-9e91-3c223e385824", + CreatedAt: time.Date(2019, time.June, 30, 4, 15, 37, 0, time.UTC), + UpdatedAt: time.Date(2019, time.June, 30, 5, 18, 49, 0, time.UTC), }, PortsBindingExt: portsbinding.PortsBindingExt{ VNICType: "normal", diff --git a/openstack/networking/v2/ports/results.go b/openstack/networking/v2/ports/results.go index 05ba595619..3bdad55403 100644 --- a/openstack/networking/v2/ports/results.go +++ b/openstack/networking/v2/ports/results.go @@ -1,6 +1,7 @@ package ports import ( + "encoding/json" "time" "github.com/gophercloud/gophercloud" @@ -114,10 +115,48 @@ type Port struct { RevisionNumber int `json:"revision_number"` // Timestamp when the port was created - CreatedAt time.Time `json:"created_at"` + CreatedAt time.Time `json:"-"` // Timestamp when the port was last updated - UpdatedAt time.Time `json:"updated_at"` + UpdatedAt time.Time `json:"-"` +} + +func (r *Port) UnmarshalJSON(b []byte) error { + type tmp Port + + // Support for older neutron time format + var s1 struct { + tmp + CreatedAt gophercloud.JSONRFC3339NoZ `json:"created_at"` + UpdatedAt gophercloud.JSONRFC3339NoZ `json:"updated_at"` + } + + err := json.Unmarshal(b, &s1) + if err == nil { + *r = Port(s1.tmp) + r.CreatedAt = time.Time(s1.CreatedAt) + r.UpdatedAt = time.Time(s1.UpdatedAt) + + return nil + } + + // Support for newer neutron time format + var s2 struct { + tmp + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` + } + + err = json.Unmarshal(b, &s2) + if err != nil { + return err + } + + *r = Port(s2.tmp) + r.CreatedAt = time.Time(s2.CreatedAt) + r.UpdatedAt = time.Time(s2.UpdatedAt) + + return nil } // PortPage is the page returned by a pager when traversing over a collection diff --git a/openstack/networking/v2/ports/testing/fixtures.go b/openstack/networking/v2/ports/testing/fixtures.go index 97ad08ac2d..83dbbd8f11 100644 --- a/openstack/networking/v2/ports/testing/fixtures.go +++ b/openstack/networking/v2/ports/testing/fixtures.go @@ -30,7 +30,9 @@ const ListResponse = ` } ], "device_id": "9ae135f4-b6e0-4dad-9e91-3c223e385824", - "port_security_enabled": false + "port_security_enabled": false, + "created_at": "2019-06-30T04:15:37", + "updated_at": "2019-06-30T05:18:49" } ] } @@ -73,7 +75,9 @@ const GetResponse = ` "fqdn": "test-port.openstack.local." } ], - "device_id": "5e3898d7-11be-483e-9732-b2f5eccd2b2e" + "device_id": "5e3898d7-11be-483e-9732-b2f5eccd2b2e", + "created_at": "2019-06-30T04:15:37Z", + "updated_at": "2019-06-30T05:18:49Z" } } ` diff --git a/openstack/networking/v2/ports/testing/requests_test.go b/openstack/networking/v2/ports/testing/requests_test.go index 7b4dd4a68c..54f2ec0c51 100644 --- a/openstack/networking/v2/ports/testing/requests_test.go +++ b/openstack/networking/v2/ports/testing/requests_test.go @@ -5,6 +5,7 @@ import ( "net/http" "net/url" "testing" + "time" fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/extradhcpopts" @@ -30,7 +31,7 @@ func TestList(t *testing.T) { count := 0 - ports.List(fake.ServiceClient(), ports.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + err := ports.List(fake.ServiceClient(), ports.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := ports.ExtractPorts(page) if err != nil { @@ -56,6 +57,8 @@ func TestList(t *testing.T) { ID: "d80b1a3b-4fc1-49f3-952e-1e2ab7081d8b", SecurityGroups: []string{}, DeviceID: "9ae135f4-b6e0-4dad-9e91-3c223e385824", + CreatedAt: time.Date(2019, time.June, 30, 4, 15, 37, 0, time.UTC), + UpdatedAt: time.Date(2019, time.June, 30, 5, 18, 49, 0, time.UTC), }, } @@ -64,6 +67,8 @@ func TestList(t *testing.T) { return true, nil }) + th.AssertNoErr(t, err) + if count != 1 { t.Errorf("Expected 1 page, got %d", count) } @@ -131,6 +136,8 @@ func TestGet(t *testing.T) { th.AssertDeepEquals(t, n.SecurityGroups, []string{}) th.AssertEquals(t, n.Status, "ACTIVE") th.AssertEquals(t, n.DeviceID, "5e3898d7-11be-483e-9732-b2f5eccd2b2e") + th.AssertEquals(t, n.CreatedAt, time.Date(2019, time.June, 30, 4, 15, 37, 0, time.UTC)) + th.AssertEquals(t, n.UpdatedAt, time.Date(2019, time.June, 30, 5, 18, 49, 0, time.UTC)) } func TestGetWithExtensions(t *testing.T) { From bca650ea280d585d3dd953d9f4e4e865b26c9397 Mon Sep 17 00:00:00 2001 From: Pierre Prinetti <pierreprinetti@redhat.com> Date: Mon, 12 Sep 2022 14:59:42 +0200 Subject: [PATCH 138/360] Port CreatedAt and UpdatedAt: add back JSON tags Removing them in 48581dacb7ec726e2ea5ed5094e0bf91db20751b was not necessary. Removing them might break client-side serialisation. --- openstack/networking/v2/ports/results.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openstack/networking/v2/ports/results.go b/openstack/networking/v2/ports/results.go index 3bdad55403..0883534ac3 100644 --- a/openstack/networking/v2/ports/results.go +++ b/openstack/networking/v2/ports/results.go @@ -115,10 +115,10 @@ type Port struct { RevisionNumber int `json:"revision_number"` // Timestamp when the port was created - CreatedAt time.Time `json:"-"` + CreatedAt time.Time `json:"created_at"` // Timestamp when the port was last updated - UpdatedAt time.Time `json:"-"` + UpdatedAt time.Time `json:"updated_at"` } func (r *Port) UnmarshalJSON(b []byte) error { From 7ec75f3c569e51b4ed8fdc8768363f5b2a4d3812 Mon Sep 17 00:00:00 2001 From: Mohammad Fatemipour <mohammad.fatemipour@sotoon.ir> Date: Sat, 10 Sep 2022 15:35:23 +0430 Subject: [PATCH 139/360] Implementing re-image volumeaction --- .../blockstorage/extensions/extensions.go | 35 +++++++++++++++++++ .../extensions/volumeactions_test.go | 20 +++++++++++ .../extensions/volumeactions/requests.go | 27 ++++++++++++++ .../extensions/volumeactions/results.go | 5 +++ .../volumeactions/testing/fixtures.go | 20 +++++++++++ .../volumeactions/testing/requests_test.go | 15 ++++++++ 6 files changed, 122 insertions(+) diff --git a/acceptance/openstack/blockstorage/extensions/extensions.go b/acceptance/openstack/blockstorage/extensions/extensions.go index d9713bf9c1..d15e4b652d 100644 --- a/acceptance/openstack/blockstorage/extensions/extensions.go +++ b/acceptance/openstack/blockstorage/extensions/extensions.go @@ -313,3 +313,38 @@ func ChangeVolumeType(t *testing.T, client *gophercloud.ServiceClient, volume *v return nil } + +// ReImage will re-image a volume +func ReImage(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume, imageID string) error { + t.Logf("Attempting to re-image volume %s", volume.ID) + + reimageOpts := volumeactions.ReImageOpts{ + ImageID: imageID, + ReImageReserved: false, + } + + err := volumeactions.ReImage(client, volume.ID, reimageOpts).ExtractErr() + if err != nil { + return err + } + + err = volumes.WaitForStatus(client, volume.ID, "available", 60) + if err != nil { + return err + } + + vol, err := v3.Get(client, volume.ID).Extract() + if err != nil { + return err + } + + if vol.VolumeImageMetadata == nil { + return fmt.Errorf("volume does not have VolumeImageMetadata map") + } + + if strings.ToLower(vol.VolumeImageMetadata["image_id"]) != imageID { + return fmt.Errorf("volume image id '%s', expected '%s'", vol.VolumeImageMetadata["image_id"], imageID) + } + + return nil +} diff --git a/acceptance/openstack/blockstorage/extensions/volumeactions_test.go b/acceptance/openstack/blockstorage/extensions/volumeactions_test.go index 264d0efc94..3c69d17a46 100644 --- a/acceptance/openstack/blockstorage/extensions/volumeactions_test.go +++ b/acceptance/openstack/blockstorage/extensions/volumeactions_test.go @@ -145,6 +145,26 @@ func TestVolumeActionsChangeType(t *testing.T) { tools.PrintResource(t, newVolume) } +func TestVolumeActionsReImage(t *testing.T) { + clients.SkipReleasesBelow(t, "stable/yoga") + + choices, err := clients.AcceptanceTestChoicesFromEnv() + if err != nil { + t.Fatal(err) + } + + blockClient, err := clients.NewBlockStorageV3Client() + th.AssertNoErr(t, err) + blockClient.Microversion = "3.68" + + volume, err := blockstorage.CreateVolume(t, blockClient) + th.AssertNoErr(t, err) + defer blockstorage.DeleteVolume(t, blockClient, volume) + + err = ReImage(t, blockClient, volume, choices.ImageID) + th.AssertNoErr(t, err) +} + // Note(jtopjian): I plan to work on this at some point, but it requires // setting up a server with iscsi utils. /* diff --git a/openstack/blockstorage/extensions/volumeactions/requests.go b/openstack/blockstorage/extensions/volumeactions/requests.go index 1c33c1785e..09dfb9ed2f 100644 --- a/openstack/blockstorage/extensions/volumeactions/requests.go +++ b/openstack/blockstorage/extensions/volumeactions/requests.go @@ -391,3 +391,30 @@ func ChangeType(client *gophercloud.ServiceClient, id string, opts ChangeTypeOpt _, 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]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "os-reimage") +} + +// ReImage will re-image a volume based on the values in ReImageOpts +func ReImage(client *gophercloud.ServiceClient, id string, opts ReImageOpts) (r ReImageResult) { + b, err := opts.ToReImageMap() + 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 +} diff --git a/openstack/blockstorage/extensions/volumeactions/results.go b/openstack/blockstorage/extensions/volumeactions/results.go index c4bd91a7ff..95b5bac1cb 100644 --- a/openstack/blockstorage/extensions/volumeactions/results.go +++ b/openstack/blockstorage/extensions/volumeactions/results.go @@ -214,3 +214,8 @@ type ForceDeleteResult struct { type ChangeTypeResult struct { gophercloud.ErrResult } + +// ReImageResult contains the response body and error from a ReImage request. +type ReImageResult struct { + gophercloud.ErrResult +} diff --git a/openstack/blockstorage/extensions/volumeactions/testing/fixtures.go b/openstack/blockstorage/extensions/volumeactions/testing/fixtures.go index 0ec9105251..378a120bc6 100644 --- a/openstack/blockstorage/extensions/volumeactions/testing/fixtures.go +++ b/openstack/blockstorage/extensions/volumeactions/testing/fixtures.go @@ -327,6 +327,26 @@ func MockSetBootableResponse(t *testing.T) { }) } +func MockReImageResponse(t *testing.T) { + th.Mux.HandleFunc("/volumes/cd281d77-8217-4830-be95-9528227c105c/action", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestHeader(t, r, "Content-Type", "application/json") + th.TestHeader(t, r, "Accept", "application/json") + th.TestJSONRequest(t, r, ` +{ + "os-reimage": { + "image_id": "71543ced-a8af-45b6-a5c4-a46282108a90", + "reimage_reserved": false + } +} + `) + w.Header().Add("Content-Type", "application/json") + w.Header().Add("Content-Length", "0") + w.WriteHeader(http.StatusAccepted) + }) +} + func MockChangeTypeResponse(t *testing.T) { th.Mux.HandleFunc("/volumes/cd281d77-8217-4830-be95-9528227c105c/action", func(w http.ResponseWriter, r *http.Request) { diff --git a/openstack/blockstorage/extensions/volumeactions/testing/requests_test.go b/openstack/blockstorage/extensions/volumeactions/testing/requests_test.go index 2b5bd9ca0a..2191a8a788 100644 --- a/openstack/blockstorage/extensions/volumeactions/testing/requests_test.go +++ b/openstack/blockstorage/extensions/volumeactions/testing/requests_test.go @@ -195,6 +195,21 @@ func TestSetBootable(t *testing.T) { th.AssertNoErr(t, err) } +func TestReImage(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockReImageResponse(t) + + options := volumeactions.ReImageOpts{ + ImageID: "71543ced-a8af-45b6-a5c4-a46282108a90", + ReImageReserved: false, + } + + err := volumeactions.ReImage(client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options).ExtractErr() + th.AssertNoErr(t, err) +} + func TestChangeType(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() From 4f89b30e6f8e40d7a5fb5ab2e96c79262af152be Mon Sep 17 00:00:00 2001 From: Duc Truong <dtruong@blizzard.com> Date: Thu, 22 Sep 2022 13:42:57 -0700 Subject: [PATCH 140/360] Return createdAt and updatedAt fields in baremetal node API results --- openstack/baremetal/v1/nodes/results.go | 8 ++++++++ .../baremetal/v1/nodes/testing/fixtures.go | 20 +++++++++++++++---- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/openstack/baremetal/v1/nodes/results.go b/openstack/baremetal/v1/nodes/results.go index 2f3d39f3d8..df956fa8d1 100644 --- a/openstack/baremetal/v1/nodes/results.go +++ b/openstack/baremetal/v1/nodes/results.go @@ -1,6 +1,8 @@ package nodes import ( + "time" + "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) @@ -234,6 +236,12 @@ type Node struct { // Static network configuration to use during deployment and cleaning. NetworkData map[string]interface{} `json:"network_data"` + + // The UTC date and time when the resource was created, ISO 8601 format. + CreatedAt time.Time `json:"created_at"` + + // The UTC date and time when the resource was updated, ISO 8601 format. May be “null”. + UpdatedAt time.Time `json:"updated_at"` } // NodePage abstracts the raw results of making a List() request against diff --git a/openstack/baremetal/v1/nodes/testing/fixtures.go b/openstack/baremetal/v1/nodes/testing/fixtures.go index 7dc7c991b2..dac21e1745 100644 --- a/openstack/baremetal/v1/nodes/testing/fixtures.go +++ b/openstack/baremetal/v1/nodes/testing/fixtures.go @@ -4,6 +4,7 @@ import ( "fmt" "net/http" "testing" + "time" "github.com/gophercloud/gophercloud/openstack/baremetal/v1/nodes" th "github.com/gophercloud/gophercloud/testhelper" @@ -164,7 +165,7 @@ const NodeListDetailBody = ` "target_provision_state": null, "target_raid_config": {}, "traits": [], - "updated_at": null, + "updated_at": "2019-02-15T19:59:29+00:00", "uuid": "d2630783-6ec8-4836-b556-ab427c4b581e", "vendor_interface": "ipmitool", "volume": [ @@ -261,7 +262,7 @@ const NodeListDetailBody = ` "target_provision_state": null, "target_raid_config": {}, "traits": [], - "updated_at": null, + "updated_at": "2019-02-15T19:59:29+00:00", "uuid": "08c84581-58f5-4ea2-a0c6-dd2e5d2b3662", "vendor_interface": "ipmitool", "volume": [ @@ -358,7 +359,7 @@ const NodeListDetailBody = ` "target_provision_state": null, "target_raid_config": {}, "traits": [], - "updated_at": null, + "updated_at": "2019-02-15T19:59:29+00:00", "uuid": "c9afd385-5d89-4ecb-9e1c-68194da6b474", "vendor_interface": "ipmitool", "volume": [ @@ -468,7 +469,7 @@ const SingleNodeBody = ` "target_provision_state": null, "target_raid_config": {}, "traits": [], - "updated_at": null, + "updated_at": "2019-02-15T19:59:29+00:00", "uuid": "d2630783-6ec8-4836-b556-ab427c4b581e", "vendor_interface": "ipmitool", "volume": [ @@ -813,6 +814,11 @@ const NodeSetMaintenanceBody = ` ` var ( + createdAtFoo, _ = time.Parse(time.RFC3339, "2019-01-31T19:59:28+00:00") + createdAtBar, _ = time.Parse(time.RFC3339, "2019-01-31T19:59:29+00:00") + createdAtBaz, _ = time.Parse(time.RFC3339, "2019-01-31T19:59:30+00:00") + updatedAt, _ = time.Parse(time.RFC3339, "2019-02-15T19:59:29+00:00") + NodeFoo = nodes.Node{ UUID: "d2630783-6ec8-4836-b556-ab427c4b581e", Name: "foo", @@ -862,6 +868,8 @@ var ( ConductorGroup: "", Protected: false, ProtectedReason: "", + CreatedAt: createdAtFoo, + UpdatedAt: updatedAt, } NodeFooValidation = nodes.NodeValidation{ @@ -959,6 +967,8 @@ var ( ConductorGroup: "", Protected: false, ProtectedReason: "", + CreatedAt: createdAtBar, + UpdatedAt: updatedAt, } NodeBaz = nodes.Node{ @@ -1003,6 +1013,8 @@ var ( ConductorGroup: "", Protected: false, ProtectedReason: "", + CreatedAt: createdAtBaz, + UpdatedAt: updatedAt, } ConfigDriveMap = nodes.ConfigDrive{ From 0f4183c0fb37edb7c4c5427c4de4d2edfc0fcb57 Mon Sep 17 00:00:00 2001 From: Duc Truong <dtruong@blizzard.com> Date: Thu, 22 Sep 2022 15:50:30 -0700 Subject: [PATCH 141/360] Add ProvisionUpdatedAt field to Baremetal nodes --- openstack/baremetal/v1/nodes/results.go | 3 +++ openstack/baremetal/v1/nodes/testing/fixtures.go | 14 ++++++++------ 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/openstack/baremetal/v1/nodes/results.go b/openstack/baremetal/v1/nodes/results.go index df956fa8d1..b2ec8696b0 100644 --- a/openstack/baremetal/v1/nodes/results.go +++ b/openstack/baremetal/v1/nodes/results.go @@ -242,6 +242,9 @@ type Node struct { // The UTC date and time when the resource was updated, ISO 8601 format. May be “null”. UpdatedAt time.Time `json:"updated_at"` + + // The UTC date and time when the provision state was updated, ISO 8601 format. May be “null”. + ProvisionUpdatedAt time.Time `json:"provision_updated_at"` } // NodePage abstracts the raw results of making a List() request against diff --git a/openstack/baremetal/v1/nodes/testing/fixtures.go b/openstack/baremetal/v1/nodes/testing/fixtures.go index dac21e1745..963a9dc234 100644 --- a/openstack/baremetal/v1/nodes/testing/fixtures.go +++ b/openstack/baremetal/v1/nodes/testing/fixtures.go @@ -144,7 +144,7 @@ const NodeListDetailBody = ` "power_state": null, "properties": {}, "provision_state": "enroll", - "provision_updated_at": null, + "provision_updated_at": "2019-02-15T17:21:29+00:00", "raid_config": {}, "raid_interface": "no-raid", "rescue_interface": "no-rescue", @@ -448,7 +448,7 @@ const SingleNodeBody = ` "power_state": null, "properties": {}, "provision_state": "enroll", - "provision_updated_at": null, + "provision_updated_at": "2019-02-15T17:21:29+00:00", "raid_config": {}, "raid_interface": "no-raid", "rescue_interface": "no-rescue", @@ -814,10 +814,11 @@ const NodeSetMaintenanceBody = ` ` var ( - createdAtFoo, _ = time.Parse(time.RFC3339, "2019-01-31T19:59:28+00:00") - createdAtBar, _ = time.Parse(time.RFC3339, "2019-01-31T19:59:29+00:00") - createdAtBaz, _ = time.Parse(time.RFC3339, "2019-01-31T19:59:30+00:00") - updatedAt, _ = time.Parse(time.RFC3339, "2019-02-15T19:59:29+00:00") + createdAtFoo, _ = time.Parse(time.RFC3339, "2019-01-31T19:59:28+00:00") + createdAtBar, _ = time.Parse(time.RFC3339, "2019-01-31T19:59:29+00:00") + createdAtBaz, _ = time.Parse(time.RFC3339, "2019-01-31T19:59:30+00:00") + updatedAt, _ = time.Parse(time.RFC3339, "2019-02-15T19:59:29+00:00") + provisonUpdatedAt, _ = time.Parse(time.RFC3339, "2019-02-15T17:21:29+00:00") NodeFoo = nodes.Node{ UUID: "d2630783-6ec8-4836-b556-ab427c4b581e", @@ -870,6 +871,7 @@ var ( ProtectedReason: "", CreatedAt: createdAtFoo, UpdatedAt: updatedAt, + ProvisionUpdatedAt: provisonUpdatedAt, } NodeFooValidation = nodes.NodeValidation{ From 2ee8cb657d7b2655add52fdb9f305dfeda980bb1 Mon Sep 17 00:00:00 2001 From: nikParasyr <nik.parasyr@protonmail.com> Date: Wed, 21 Sep 2022 10:05:43 +0200 Subject: [PATCH 142/360] Support for service types in neutron subnet --- .../openstack/networking/v2/networking.go | 39 +++++++++++++++++++ .../openstack/networking/v2/subnets_test.go | 27 +++++++++++++ openstack/networking/v2/subnets/doc.go | 3 ++ openstack/networking/v2/subnets/requests.go | 6 +++ openstack/networking/v2/subnets/results.go | 3 ++ .../networking/v2/subnets/testing/fixtures.go | 4 +- .../v2/subnets/testing/requests_test.go | 8 ++-- 7 files changed, 86 insertions(+), 4 deletions(-) diff --git a/acceptance/openstack/networking/v2/networking.go b/acceptance/openstack/networking/v2/networking.go index 9362c9b623..aae456b198 100644 --- a/acceptance/openstack/networking/v2/networking.go +++ b/acceptance/openstack/networking/v2/networking.go @@ -312,6 +312,45 @@ func CreateSubnet(t *testing.T, client *gophercloud.ServiceClient, networkID str return subnet, nil } +// CreateSubnet will create a subnet on the specified Network ID and service types. +// +// An error will be returned if the subnet could not be created. +func CreateSubnetWithServiceTypes(t *testing.T, client *gophercloud.ServiceClient, networkID string) (*subnets.Subnet, error) { + subnetName := tools.RandomString("TESTACC-", 8) + subnetDescription := tools.RandomString("TESTACC-DESC-", 8) + subnetOctet := tools.RandomInt(1, 250) + subnetCIDR := fmt.Sprintf("192.168.%d.0/24", subnetOctet) + subnetGateway := fmt.Sprintf("192.168.%d.1", subnetOctet) + serviceTypes := []string{"network:routed"} + createOpts := subnets.CreateOpts{ + NetworkID: networkID, + CIDR: subnetCIDR, + IPVersion: 4, + Name: subnetName, + Description: subnetDescription, + EnableDHCP: gophercloud.Disabled, + GatewayIP: &subnetGateway, + ServiceTypes: serviceTypes, + } + + t.Logf("Attempting to create subnet: %s", subnetName) + + subnet, err := subnets.Create(client, createOpts).Extract() + if err != nil { + return subnet, err + } + + t.Logf("Successfully created subnet.") + + th.AssertEquals(t, subnet.Name, subnetName) + th.AssertEquals(t, subnet.Description, subnetDescription) + th.AssertEquals(t, subnet.GatewayIP, subnetGateway) + th.AssertEquals(t, subnet.CIDR, subnetCIDR) + th.AssertDeepEquals(t, subnet.ServiceTypes, serviceTypes) + + return subnet, nil +} + // CreateSubnetWithDefaultGateway will create a subnet on the specified Network // ID and have Neutron set the gateway by default An error will be returned if // the subnet could not be created. diff --git a/acceptance/openstack/networking/v2/subnets_test.go b/acceptance/openstack/networking/v2/subnets_test.go index 95f5a6833a..6b45d7c6a0 100644 --- a/acceptance/openstack/networking/v2/subnets_test.go +++ b/acceptance/openstack/networking/v2/subnets_test.go @@ -65,6 +65,33 @@ func TestSubnetCRUD(t *testing.T) { th.AssertEquals(t, found, true) } +func TestSubnetsServiceType(t *testing.T) { + client, err := clients.NewNetworkV2Client() + th.AssertNoErr(t, err) + + // Create Network + network, err := CreateNetwork(t, client) + th.AssertNoErr(t, err) + defer DeleteNetwork(t, client, network.ID) + + // Create Subnet + subnet, err := CreateSubnetWithServiceTypes(t, client, network.ID) + th.AssertNoErr(t, err) + defer DeleteSubnet(t, client, subnet.ID) + + tools.PrintResource(t, subnet) + + serviceTypes := []string{"network:floatingip"} + updateOpts := subnets.UpdateOpts{ + ServiceTypes: &serviceTypes, + } + + newSubnet, err := subnets.Update(client, subnet.ID, updateOpts).Extract() + th.AssertNoErr(t, err) + + th.AssertDeepEquals(t, newSubnet.ServiceTypes, serviceTypes) +} + func TestSubnetsDefaultGateway(t *testing.T) { client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) diff --git a/openstack/networking/v2/subnets/doc.go b/openstack/networking/v2/subnets/doc.go index 7d3a1b9b65..8bb4468c4e 100644 --- a/openstack/networking/v2/subnets/doc.go +++ b/openstack/networking/v2/subnets/doc.go @@ -44,6 +44,7 @@ Example to Create a Subnet With Specified Gateway }, }, DNSNameservers: []string{"foo"}, + ServiceTypes: []string{"network:floatingip"}, } subnet, err := subnets.Create(networkClient, createOpts).Extract() @@ -98,11 +99,13 @@ Example to Update a Subnet subnetID := "db77d064-e34f-4d06-b060-f21e28a61c23" dnsNameservers := []string{"8.8.8.8"} + serviceTypes := []string{"network:floatingip", "network:routed"} name := "new_name" updateOpts := subnets.UpdateOpts{ Name: &name, DNSNameservers: &dnsNameservers, + ServiceTypes: &serviceTypes, } subnet, err := subnets.Update(networkClient, subnetID, updateOpts).Extract() diff --git a/openstack/networking/v2/subnets/requests.go b/openstack/networking/v2/subnets/requests.go index 7d97fb259d..2e87907587 100644 --- a/openstack/networking/v2/subnets/requests.go +++ b/openstack/networking/v2/subnets/requests.go @@ -122,6 +122,9 @@ type CreateOpts struct { // DNSNameservers are the nameservers to be set via DHCP. DNSNameservers []string `json:"dns_nameservers,omitempty"` + // ServiceTypes are the service types associated with the subnet. + ServiceTypes []string `json:"service_types,omitempty"` + // HostRoutes are any static host routes to be set via DHCP. HostRoutes []HostRoute `json:"host_routes,omitempty"` @@ -194,6 +197,9 @@ type UpdateOpts struct { // DNSNameservers are the nameservers to be set via DHCP. DNSNameservers *[]string `json:"dns_nameservers,omitempty"` + // ServiceTypes are the service types associated with the subnet. + ServiceTypes *[]string `json:"service_types,omitempty"` + // HostRoutes are any static host routes to be set via DHCP. HostRoutes *[]HostRoute `json:"host_routes,omitempty"` diff --git a/openstack/networking/v2/subnets/results.go b/openstack/networking/v2/subnets/results.go index e04d486fd4..63b98f7248 100644 --- a/openstack/networking/v2/subnets/results.go +++ b/openstack/networking/v2/subnets/results.go @@ -83,6 +83,9 @@ type Subnet struct { // DNS name servers used by hosts in this subnet. DNSNameservers []string `json:"dns_nameservers"` + // Service types associated with the subnet. + ServiceTypes []string `json:"service_types"` + // Sub-ranges of CIDR available for dynamic allocation to ports. // See AllocationPool. AllocationPools []AllocationPool `json:"allocation_pools"` diff --git a/openstack/networking/v2/subnets/testing/fixtures.go b/openstack/networking/v2/subnets/testing/fixtures.go index 38cdbc8559..af8512e549 100644 --- a/openstack/networking/v2/subnets/testing/fixtures.go +++ b/openstack/networking/v2/subnets/testing/fixtures.go @@ -193,6 +193,7 @@ const SubnetCreateRequest = ` "gateway_ip": "192.168.199.1", "cidr": "192.168.199.0/24", "dns_nameservers": ["foo"], + "service_types": ["network:routed"], "allocation_pools": [ { "start": "192.168.199.2", @@ -212,7 +213,8 @@ const SubnetCreateResult = ` "enable_dhcp": true, "network_id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", "tenant_id": "4fd44f30292945e481c7b8a0c8908869", - "dns_nameservers": [], + "dns_nameservers": ["foo"], + "service_types": ["network:routed"], "allocation_pools": [ { "start": "192.168.199.2", diff --git a/openstack/networking/v2/subnets/testing/requests_test.go b/openstack/networking/v2/subnets/testing/requests_test.go index 34a008599f..7e82d5855d 100644 --- a/openstack/networking/v2/subnets/testing/requests_test.go +++ b/openstack/networking/v2/subnets/testing/requests_test.go @@ -118,6 +118,7 @@ func TestCreate(t *testing.T) { }, }, DNSNameservers: []string{"foo"}, + ServiceTypes: []string{"network:routed"}, HostRoutes: []subnets.HostRoute{ {NextHop: "bar"}, }, @@ -130,7 +131,8 @@ func TestCreate(t *testing.T) { th.AssertEquals(t, s.EnableDHCP, true) th.AssertEquals(t, s.NetworkID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") th.AssertEquals(t, s.TenantID, "4fd44f30292945e481c7b8a0c8908869") - th.AssertDeepEquals(t, s.DNSNameservers, []string{}) + th.AssertDeepEquals(t, s.DNSNameservers, []string{"foo"}) + th.AssertDeepEquals(t, s.ServiceTypes, []string{"network:routed"}) th.AssertDeepEquals(t, s.AllocationPools, []subnets.AllocationPool{ { Start: "192.168.199.2", @@ -319,7 +321,7 @@ func TestCreateWithNoCIDR(t *testing.T) { th.AssertEquals(t, s.EnableDHCP, true) th.AssertEquals(t, s.NetworkID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") th.AssertEquals(t, s.TenantID, "4fd44f30292945e481c7b8a0c8908869") - th.AssertDeepEquals(t, s.DNSNameservers, []string{}) + th.AssertDeepEquals(t, s.DNSNameservers, []string{"foo"}) th.AssertDeepEquals(t, s.AllocationPools, []subnets.AllocationPool{ { Start: "192.168.199.2", @@ -368,7 +370,7 @@ func TestCreateWithPrefixlen(t *testing.T) { th.AssertEquals(t, s.EnableDHCP, true) th.AssertEquals(t, s.NetworkID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") th.AssertEquals(t, s.TenantID, "4fd44f30292945e481c7b8a0c8908869") - th.AssertDeepEquals(t, s.DNSNameservers, []string{}) + th.AssertDeepEquals(t, s.DNSNameservers, []string{"foo"}) th.AssertDeepEquals(t, s.AllocationPools, []subnets.AllocationPool{ { Start: "192.168.199.2", From 4d3ca6eca05a80ce954f224a0c9004221893d831 Mon Sep 17 00:00:00 2001 From: emilmaruszczak <emil.maruszczak@thehutgroup.com> Date: Fri, 30 Sep 2022 10:28:52 +0200 Subject: [PATCH 143/360] Add acceptance tests --- acceptance/openstack/identity/v3/limits_test.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/acceptance/openstack/identity/v3/limits_test.go b/acceptance/openstack/identity/v3/limits_test.go index 1bb7b1e921..2fe5787c58 100644 --- a/acceptance/openstack/identity/v3/limits_test.go +++ b/acceptance/openstack/identity/v3/limits_test.go @@ -7,10 +7,23 @@ import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/identity/v3/limits" th "github.com/gophercloud/gophercloud/testhelper" ) +func TestGetEnforcementModel(t *testing.T) { + clients.RequireAdmin(t) + + client, err := clients.NewIdentityV3Client() + th.AssertNoErr(t, err) + + model, err := limits.GetEnforcementModel(client).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, model) +} + func TestLimitsList(t *testing.T) { clients.RequireAdmin(t) From 65d66ba6b4c5dde3ff4469a11f211ba486949cb9 Mon Sep 17 00:00:00 2001 From: emilmaruszczak <emil.maruszczak@thehutgroup.com> Date: Tue, 6 Sep 2022 16:41:51 +0200 Subject: [PATCH 144/360] Add BatchCreate operation --- openstack/identity/v3/limits/doc.go | 24 +++++++ openstack/identity/v3/limits/requests.go | 65 ++++++++++++++++++- openstack/identity/v3/limits/results.go | 26 ++++++-- .../identity/v3/limits/testing/fixtures.go | 36 ++++++++++ .../v3/limits/testing/requests_test.go | 27 ++++++++ openstack/identity/v3/limits/urls.go | 11 +++- 6 files changed, 180 insertions(+), 9 deletions(-) diff --git a/openstack/identity/v3/limits/doc.go b/openstack/identity/v3/limits/doc.go index 4f97669eff..ed02b478e4 100644 --- a/openstack/identity/v3/limits/doc.go +++ b/openstack/identity/v3/limits/doc.go @@ -24,5 +24,29 @@ Example to List Limits if err != nil { panic(err) } + +Example to Create Limits + + batchCreateOpts := limits.BatchCreateOpts{ + limits.CreateOpts{ + ServiceID: "9408080f1970482aa0e38bc2d4ea34b7", + ProjectID: "3a705b9f56bb439381b43c4fe59dccce", + RegionID: "RegionOne", + ResourceName: "snapshot", + ResourceLimit: 5, + }, + limits.CreateOpts{ + ServiceID: "9408080f1970482aa0e38bc2d4ea34b7", + DomainID: "edbafc92be354ffa977c58aa79c7bdb2", + ResourceName: "volume", + ResourceLimit: 10, + Description: "Number of volumes for project 3a705b9f56bb439381b43c4fe59dccce", + }, + } + + createdLimits, err := limits.Create(identityClient, batchCreateOpts).Extract() + if err != nil { + panic(err) + } */ package limits diff --git a/openstack/identity/v3/limits/requests.go b/openstack/identity/v3/limits/requests.go index 1bdac9dc30..c6adeb002f 100644 --- a/openstack/identity/v3/limits/requests.go +++ b/openstack/identity/v3/limits/requests.go @@ -44,7 +44,7 @@ func (opts ListOpts) ToLimitListQuery() (string, error) { // List enumerates the limits. func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { - url := listURL(client) + url := rootURL(client) if opts != nil { query, err := opts.ToLimitListQuery() if err != nil { @@ -56,3 +56,66 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa return LimitPage{pagination.LinkedPageBase{PageResult: r}} }) } + +// BatchCreateOptsBuilder allows extensions to add additional parameters to +// the Create request. +type BatchCreateOptsBuilder interface { + ToLimitsCreateMap() (map[string]interface{}, error) +} + +type CreateOpts struct { + // RegionID is the ID of the region where the limit is applied. + RegionID string `json:"region_id,omitempty"` + + // ProjectID is the ID of the project where the limit is applied. + ProjectID string `json:"project_id,omitempty"` + + // DomainID is the ID of the domain where the limit is applied. + DomainID string `json:"domain_id,omitempty"` + + // ServiceID is the ID of the service where the limit is applied. + ServiceID string `json:"service_id" required:"true"` + + // Description of the limit. + Description string `json:"description,omitempty"` + + // ResourceName is the name of the resource that the limit is applied to. + ResourceName string `json:"resource_name" required:"true"` + + // ResourceLimit is the override limit. + ResourceLimit int `json:"resource_limit"` +} + +// BatchCreateOpts provides options used to create limits. +type BatchCreateOpts []CreateOpts + +// ToLimitsCreateMap formats a BatchCreateOpts into a create request. +func (opts BatchCreateOpts) ToLimitsCreateMap() (map[string]interface{}, error) { + limits := make([]map[string]interface{}, len(opts)) + for i, limit := range opts { + limitMap, err := limit.ToMap() + if err != nil { + return nil, err + } + limits[i] = limitMap + } + return map[string]interface{}{"limits": limits}, nil +} + +func (opts CreateOpts) ToMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "") +} + +// BatchCreate creates new Limits. +func BatchCreate(client *gophercloud.ServiceClient, opts BatchCreateOptsBuilder) (r CreateResult) { + b, err := opts.ToLimitsCreateMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Post(rootURL(client), &b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{201}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} diff --git a/openstack/identity/v3/limits/results.go b/openstack/identity/v3/limits/results.go index 16ba63bc77..2f55fb7905 100644 --- a/openstack/identity/v3/limits/results.go +++ b/openstack/identity/v3/limits/results.go @@ -59,11 +59,22 @@ type Limit struct { Links map[string]interface{} `json:"links"` } +// A LimitsOutput is an array of limits returned by List and BatchCreate operations +type LimitsOutput struct { + Limits []Limit `json:"limits"` +} + // LimitPage is a single page of Limit results. type LimitPage struct { pagination.LinkedPageBase } +// CreateResult is the response from a Create operation. Call its Extract method +// to interpret it as a Limits. +type CreateResult struct { + gophercloud.Result +} + // IsEmpty determines whether or not a page of Limits contains any results. func (r LimitPage) IsEmpty() (bool, error) { limits, err := ExtractLimits(r) @@ -88,9 +99,14 @@ func (r LimitPage) NextPageURL() (string, error) { // ExtractLimits returns a slice of Limits contained in a single page of // results. func ExtractLimits(r pagination.Page) ([]Limit, error) { - var s struct { - Limits []Limit `json:"limits"` - } - err := (r.(LimitPage)).ExtractInto(&s) - return s.Limits, err + var out LimitsOutput + err := (r.(LimitPage)).ExtractInto(&out) + return out.Limits, err +} + +// Extract interprets CreateResult as slice of Limits. +func (r CreateResult) Extract() ([]Limit, error) { + var out LimitsOutput + err := r.ExtractInto(&out) + return out.Limits, err } diff --git a/openstack/identity/v3/limits/testing/fixtures.go b/openstack/identity/v3/limits/testing/fixtures.go index ed502b3d01..39afaca423 100644 --- a/openstack/identity/v3/limits/testing/fixtures.go +++ b/openstack/identity/v3/limits/testing/fixtures.go @@ -58,12 +58,35 @@ const ListOutput = ` } ` +const CreateRequest = ` +{ + "limits":[ + { + "service_id": "9408080f1970482aa0e38bc2d4ea34b7", + "project_id": "3a705b9f56bb439381b43c4fe59dccce", + "region_id": "RegionOne", + "resource_name": "snapshot", + "resource_limit": 5 + }, + { + "service_id": "9408080f1970482aa0e38bc2d4ea34b7", + "domain_id": "edbafc92be354ffa977c58aa79c7bdb2", + "resource_name": "volume", + "resource_limit": 11, + "description": "Number of volumes for project 3a705b9f56bb439381b43c4fe59dccce" + } + ] +} +` + // Model is the enforcement model in the GetEnforcementModel request. var Model = limits.EnforcementModel{ Name: "flat", Description: "Limit enforcement and validation does not take project hierarchy into consideration.", } +const CreateOutput = ListOutput + // FirstLimit is the first limit in the List request. var FirstLimit = limits.Limit{ ResourceName: "volume", @@ -120,3 +143,16 @@ func HandleListLimitsSuccessfully(t *testing.T) { fmt.Fprintf(w, ListOutput) }) } + +// HandleCreateLimitSuccessfully creates an HTTP handler at `/limits` on the +// test handler mux that tests limit creation. +func HandleCreateLimitSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/limits", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + th.TestJSONRequest(t, r, CreateRequest) + + w.WriteHeader(http.StatusCreated) + fmt.Fprintf(w, CreateOutput) + }) +} diff --git a/openstack/identity/v3/limits/testing/requests_test.go b/openstack/identity/v3/limits/testing/requests_test.go index ec990357c3..1f7a2037f2 100644 --- a/openstack/identity/v3/limits/testing/requests_test.go +++ b/openstack/identity/v3/limits/testing/requests_test.go @@ -50,3 +50,30 @@ func TestListLimitsAllPages(t *testing.T) { th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedLimitsSlice, actual) } + +func TestCreateLimits(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleCreateLimitSuccessfully(t) + + createOpts := limits.BatchCreateOpts{ + limits.CreateOpts{ + ServiceID: "9408080f1970482aa0e38bc2d4ea34b7", + ProjectID: "3a705b9f56bb439381b43c4fe59dccce", + RegionID: "RegionOne", + ResourceName: "snapshot", + ResourceLimit: 5, + }, + limits.CreateOpts{ + ServiceID: "9408080f1970482aa0e38bc2d4ea34b7", + DomainID: "edbafc92be354ffa977c58aa79c7bdb2", + ResourceName: "volume", + ResourceLimit: 11, + Description: "Number of volumes for project 3a705b9f56bb439381b43c4fe59dccce", + }, + } + + actual, err := limits.BatchCreate(client.ServiceClient(), createOpts).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, ExpectedLimitsSlice, actual) +} diff --git a/openstack/identity/v3/limits/urls.go b/openstack/identity/v3/limits/urls.go index 022c8d9e52..79498df3bd 100644 --- a/openstack/identity/v3/limits/urls.go +++ b/openstack/identity/v3/limits/urls.go @@ -2,10 +2,15 @@ package limits import "github.com/gophercloud/gophercloud" +const ( + rootPath = "limits" + enforcementModelPath = "model" +) + func enforcementModelURL(client *gophercloud.ServiceClient) string { - return client.ServiceURL("limits", "model") + return client.ServiceURL(rootPath, enforcementModelPath) } -func listURL(client *gophercloud.ServiceClient) string { - return client.ServiceURL("limits") +func rootURL(client *gophercloud.ServiceClient) string { + return client.ServiceURL(rootPath) } From fe20aa56c44b3cf9115a9eeea7da8ba84eed1d07 Mon Sep 17 00:00:00 2001 From: emilmaruszczak <emil.maruszczak@thehutgroup.com> Date: Tue, 4 Oct 2022 10:18:53 +0200 Subject: [PATCH 145/360] Add acceptance test --- .../openstack/identity/v3/limits_test.go | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/acceptance/openstack/identity/v3/limits_test.go b/acceptance/openstack/identity/v3/limits_test.go index 2fe5787c58..a4d6996cdf 100644 --- a/acceptance/openstack/identity/v3/limits_test.go +++ b/acceptance/openstack/identity/v3/limits_test.go @@ -9,6 +9,7 @@ import ( "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/identity/v3/limits" + "github.com/gophercloud/gophercloud/openstack/identity/v3/services" th "github.com/gophercloud/gophercloud/testhelper" ) @@ -38,3 +39,44 @@ func TestLimitsList(t *testing.T) { _, err = limits.ExtractLimits(allPages) th.AssertNoErr(t, err) } + +func TestCreateLimits(t *testing.T) { + limitDescription := tools.RandomString("TESTLIMITS-DESC-", 8) + resourceLimit := tools.RandomInt(1, 100) + resourceName := "volume" + + clients.RequireAdmin(t) + + client, err := clients.NewIdentityV3Client() + th.AssertNoErr(t, err) + + project, err := CreateProject(t, client, nil) + th.AssertNoErr(t, err) + + createServiceOpts := &services.CreateOpts{ + Type: resourceName, + Extra: map[string]interface{}{}, + } + + service, err := CreateService(t, client, createServiceOpts) + th.AssertNoErr(t, err) + + createOpts := limits.BatchCreateOpts{ + limits.CreateOpts{ + ServiceID: service.ID, + ProjectID: project.ID, + ResourceName: resourceName, + ResourceLimit: resourceLimit, + Description: limitDescription, + }, + } + + createdLimits, err := limits.BatchCreate(client, createOpts).Extract() + th.AssertNoErr(t, err) + th.AssertIntGreaterOrEqual(t, 1, len(createdLimits)) + th.AssertEquals(t, limitDescription, createdLimits[0].Description) + th.AssertEquals(t, resourceLimit, createdLimits[0].ResourceLimit) + th.AssertEquals(t, resourceName, createdLimits[0].ResourceName) + th.AssertEquals(t, service.ID, createdLimits[0].ServiceID) + th.AssertEquals(t, project.ID, createdLimits[0].ProjectID) +} From 9f0ca02b742e8b21b5ee07e245ec719f0ec963b6 Mon Sep 17 00:00:00 2001 From: emilmaruszczak <emil.maruszczak@thehutgroup.com> Date: Thu, 8 Sep 2022 11:01:51 +0200 Subject: [PATCH 146/360] Add mappings list operation --- .../identity/v3/extensions/federation/doc.go | 16 ++ .../v3/extensions/federation/requests.go | 13 ++ .../v3/extensions/federation/results.go | 167 ++++++++++++++++++ .../extensions/federation/testing/fixtures.go | 109 ++++++++++++ .../federation/testing/requests_test.go | 42 +++++ .../identity/v3/extensions/federation/urls.go | 12 ++ 6 files changed, 359 insertions(+) create mode 100644 openstack/identity/v3/extensions/federation/doc.go create mode 100644 openstack/identity/v3/extensions/federation/requests.go create mode 100644 openstack/identity/v3/extensions/federation/results.go create mode 100644 openstack/identity/v3/extensions/federation/testing/fixtures.go create mode 100644 openstack/identity/v3/extensions/federation/testing/requests_test.go create mode 100644 openstack/identity/v3/extensions/federation/urls.go diff --git a/openstack/identity/v3/extensions/federation/doc.go b/openstack/identity/v3/extensions/federation/doc.go new file mode 100644 index 0000000000..e8b9369aaf --- /dev/null +++ b/openstack/identity/v3/extensions/federation/doc.go @@ -0,0 +1,16 @@ +/* +Package federation provides information and interaction with OS-FEDERATION API for the +Openstack Identity service. + +Example to List Mappings + + allPages, err := federation.ListMappings(identityClient).AllPages() + if err != nil { + panic(err) + } + allMappings, err := federation.ExtractMappings(allPages) + if err != nil { + panic(err) + } +*/ +package federation diff --git a/openstack/identity/v3/extensions/federation/requests.go b/openstack/identity/v3/extensions/federation/requests.go new file mode 100644 index 0000000000..d42caf2e0c --- /dev/null +++ b/openstack/identity/v3/extensions/federation/requests.go @@ -0,0 +1,13 @@ +package federation + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// ListMappings enumerates the mappings. +func ListMappings(client *gophercloud.ServiceClient) pagination.Pager { + return pagination.NewPager(client, mappingsRootURL(client), func(r pagination.PageResult) pagination.Page { + return MappingsPage{pagination.LinkedPageBase{PageResult: r}} + }) +} diff --git a/openstack/identity/v3/extensions/federation/results.go b/openstack/identity/v3/extensions/federation/results.go new file mode 100644 index 0000000000..745546fd7b --- /dev/null +++ b/openstack/identity/v3/extensions/federation/results.go @@ -0,0 +1,167 @@ +package federation + +import ( + "github.com/gophercloud/gophercloud/pagination" +) + +type UserType string + +const ( + UserTypeEphemeral UserType = "ephemeral" + UserTypeLocal UserType = "local" +) + +// Mapping a set of rules to map federation protocol attributes to +// Identity API objects. +type Mapping struct { + // The Federation Mapping unique ID + ID string `json:"id"` + + // Links contains referencing links to the limit. + Links map[string]interface{} `json:"links"` + + // The list of rules used to map remote users into local users + Rules []MappingRule `json:"rules"` +} + +type MappingRule struct { + // References a local Identity API resource, such as a group or user to which the remote attributes will be mapped. + Local []RuleLocal `json:"local"` + + // Each object contains a rule for mapping remote attributes to Identity API concepts. + Remote []RuleRemote `json:"remote"` +} + +type RuleRemote struct { + // Type represents an assertion type keyword. + Type string `json:"type"` + + // If true, then each string will be evaluated as a regular expression search against the remote attribute type. + Regex *bool `json:"regex,omitempty"` + + // The rule is matched only if any of the specified strings appear in the remote attribute type. + // This is mutually exclusive with NotAnyOf. + AnyOneOf []string `json:"any_one_of,omitempty"` + + // The rule is not matched if any of the specified strings appear in the remote attribute type. + // This is mutually exclusive with AnyOneOf. + NotAnyOf []string `json:"not_any_of,omitempty"` + + // The rule works as a filter, removing any specified strings that are listed there from the remote attribute type. + // This is mutually exclusive with Whitelist. + Blacklist []string `json:"blacklist,omitempty"` + + // The rule works as a filter, allowing only the specified strings in the remote attribute type to be passed ahead. + // This is mutually exclusive with Blacklist. + Whitelist []string `json:"whitelist,omitempty"` +} + +type RuleLocal struct { + // Domain to which the remote attributes will be matched. + Domain *Domain `json:"domain,omitempty"` + + // Group to which the remote attributes will be matched. + Group *Group `json:"group,omitempty"` + + // Group IDs to which the remote attributes will be matched. + GroupIDs string `json:"group_ids,omitempty"` + + // Groups to which the remote attributes will be matched. + Groups string `json:"groups,omitempty"` + + // Projects to which the remote attributes will be matched. + Projects []RuleProject `json:"projects,omitempty"` + + // User to which the remote attributes will be matched. + User *RuleUser `json:"user,omitempty"` +} + +type Domain struct { + // Domain ID + // This is mutually exclusive with Name. + ID string `json:"id,omitempty"` + + // Domain Name + // This is mutually exclusive with ID. + Name string `json:"name,omitempty"` +} + +type Group struct { + // Group ID to which the rule should match. + // This is mutually exclusive with Name and Domain. + ID string `json:"id,omitempty"` + + // Group Name to which the rule should match. + // This is mutually exclusive with ID. + Name string `json:"name,omitempty"` + + // Group Domain to which the rule should match. + // This is mutually exclusive with ID. + Domain *Domain `json:"domain,omitempty"` +} + +type RuleProject struct { + // Project name + Name string `json:"name,omitempty"` + + // Project roles + Roles []RuleProjectRole `json:"roles,omitempty"` +} + +type RuleProjectRole struct { + // Role name + Name string `json:"name,omitempty"` +} + +type RuleUser struct { + // User domain + Domain *Domain `json:"domain,omitempty"` + + // User email + Email string `json:"email,omitempty"` + + // User ID + ID string `json:"id,omitempty"` + + // User name + Name string `json:"name,omitempty"` + + // User type + Type *UserType `json:"type,omitempty"` +} + +// MappingsPage is a single page of Mapping results. +type MappingsPage struct { + pagination.LinkedPageBase +} + +// IsEmpty determines whether or not a page of Mappings contains any results. +func (c MappingsPage) IsEmpty() (bool, error) { + mappings, err := ExtractMappings(c) + return len(mappings) == 0, err +} + +// NextPageURL extracts the "next" link from the links section of the result. +func (c MappingsPage) 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 +} + +// ExtractMappings returns a slice of Mappings contained in a single page of +// results. +func ExtractMappings(r pagination.Page) ([]Mapping, error) { + var s struct { + Mappings []Mapping `json:"mappings"` + } + err := (r.(MappingsPage)).ExtractInto(&s) + return s.Mappings, err +} diff --git a/openstack/identity/v3/extensions/federation/testing/fixtures.go b/openstack/identity/v3/extensions/federation/testing/fixtures.go new file mode 100644 index 0000000000..59785dfed3 --- /dev/null +++ b/openstack/identity/v3/extensions/federation/testing/fixtures.go @@ -0,0 +1,109 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + "github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/federation" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +const ListOutput = ` +{ + "links": { + "next": null, + "previous": null, + "self": "http://example.com/identity/v3/OS-FEDERATION/mappings" + }, + "mappings": [ + { + "id": "ACME", + "links": { + "self": "http://example.com/identity/v3/OS-FEDERATION/mappings/ACME" + }, + "rules": [ + { + "local": [ + { + "user": { + "name": "{0}" + } + }, + { + "group": { + "id": "0cd5e9" + } + } + ], + "remote": [ + { + "type": "UserName" + }, + { + "type": "orgPersonType", + "not_any_of": [ + "Contractor", + "Guest" + ] + } + ] + } + ] + } + ] +} +` + +var MappingACME = federation.Mapping{ + ID: "ACME", + Links: map[string]interface{}{ + "self": "http://example.com/identity/v3/OS-FEDERATION/mappings/ACME", + }, + Rules: []federation.MappingRule{ + { + Local: []federation.RuleLocal{ + { + User: &federation.RuleUser{ + Name: "{0}", + }, + }, + { + Group: &federation.Group{ + ID: "0cd5e9", + }, + }, + }, + Remote: []federation.RuleRemote{ + { + Type: "UserName", + }, + { + Type: "orgPersonType", + NotAnyOf: []string{ + "Contractor", + "Guest", + }, + }, + }, + }, + }, +} + +// ExpectedMappingsSlice is the slice of mappings expected to be returned from ListOutput. +var ExpectedMappingsSlice = []federation.Mapping{MappingACME} + +// HandleListMappingsSuccessfully creates an HTTP handler at `/mappings` on the +// test handler mux that responds with a list of two mappings. +func HandleListMappingsSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/OS-FEDERATION/mappings", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "Accept", "application/json") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, ListOutput) + }) +} diff --git a/openstack/identity/v3/extensions/federation/testing/requests_test.go b/openstack/identity/v3/extensions/federation/testing/requests_test.go new file mode 100644 index 0000000000..0bf53a4529 --- /dev/null +++ b/openstack/identity/v3/extensions/federation/testing/requests_test.go @@ -0,0 +1,42 @@ +package testing + +import ( + "testing" + + "github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/federation" + "github.com/gophercloud/gophercloud/pagination" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestListMappings(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListMappingsSuccessfully(t) + + count := 0 + err := federation.ListMappings(client.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) { + count++ + + actual, err := federation.ExtractMappings(page) + th.AssertNoErr(t, err) + + th.CheckDeepEquals(t, ExpectedMappingsSlice, actual) + + return true, nil + }) + th.AssertNoErr(t, err) + th.CheckEquals(t, count, 1) +} + +func TestListMappingsAllPages(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListMappingsSuccessfully(t) + + allPages, err := federation.ListMappings(client.ServiceClient()).AllPages() + th.AssertNoErr(t, err) + actual, err := federation.ExtractMappings(allPages) + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, ExpectedMappingsSlice, actual) +} diff --git a/openstack/identity/v3/extensions/federation/urls.go b/openstack/identity/v3/extensions/federation/urls.go new file mode 100644 index 0000000000..8841262dca --- /dev/null +++ b/openstack/identity/v3/extensions/federation/urls.go @@ -0,0 +1,12 @@ +package federation + +import "github.com/gophercloud/gophercloud" + +const ( + rootPath = "OS-FEDERATION" + mappingsPath = "mappings" +) + +func mappingsRootURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL(rootPath, mappingsPath) +} From aeccb4544483607010c8f4448a504f2f48a68075 Mon Sep 17 00:00:00 2001 From: emilmaruszczak <emil.maruszczak@thehutgroup.com> Date: Tue, 4 Oct 2022 18:14:27 +0200 Subject: [PATCH 147/360] Add acceptance test --- .../openstack/identity/v3/federation_test.go | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 acceptance/openstack/identity/v3/federation_test.go diff --git a/acceptance/openstack/identity/v3/federation_test.go b/acceptance/openstack/identity/v3/federation_test.go new file mode 100644 index 0000000000..21f7447ece --- /dev/null +++ b/acceptance/openstack/identity/v3/federation_test.go @@ -0,0 +1,26 @@ +//go:build acceptance +// +build acceptance + +package v3 + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/federation" +) + +func TestListMappings(t *testing.T) { + client, err := clients.NewIdentityV3Client() + th.AssertNoErr(t, err) + + allPages, err := federation.ListMappings(client).AllPages() + th.AssertNoErr(t, err) + + mappings, err := federation.ExtractMappings(allPages) + th.AssertNoErr(t, err) + + tools.PrintResource(t, mappings) +} From 4b28068aa97548cc08ccd8ff2f04f24272d4a51b Mon Sep 17 00:00:00 2001 From: emilmaruszczak <emil.maruszczak@thehutgroup.com> Date: Wed, 5 Oct 2022 10:16:33 +0200 Subject: [PATCH 148/360] Run go fmt --- acceptance/openstack/identity/v3/federation_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/acceptance/openstack/identity/v3/federation_test.go b/acceptance/openstack/identity/v3/federation_test.go index 21f7447ece..8afc7f9ad2 100644 --- a/acceptance/openstack/identity/v3/federation_test.go +++ b/acceptance/openstack/identity/v3/federation_test.go @@ -8,18 +8,18 @@ import ( "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" - th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/federation" + th "github.com/gophercloud/gophercloud/testhelper" ) func TestListMappings(t *testing.T) { client, err := clients.NewIdentityV3Client() th.AssertNoErr(t, err) - allPages, err := federation.ListMappings(client).AllPages() + allPages, err := federation.ListMappings(client).AllPages() th.AssertNoErr(t, err) - mappings, err := federation.ExtractMappings(allPages) + mappings, err := federation.ExtractMappings(allPages) th.AssertNoErr(t, err) tools.PrintResource(t, mappings) From 01d57b79670f9ba26c96a5ba0e57699bdc5b8e3e Mon Sep 17 00:00:00 2001 From: nikParasyr <nik.parasyr@protonmail.com> Date: Wed, 12 Oct 2022 17:46:48 +0200 Subject: [PATCH 149/360] Add Prometheus protocol for octavia listeners Prometheus protocol was added for octavia listeners on 2.25 --- openstack/loadbalancer/v2/listeners/requests.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/openstack/loadbalancer/v2/listeners/requests.go b/openstack/loadbalancer/v2/listeners/requests.go index 54e968ce51..a0a06f6448 100644 --- a/openstack/loadbalancer/v2/listeners/requests.go +++ b/openstack/loadbalancer/v2/listeners/requests.go @@ -18,7 +18,9 @@ const ( ProtocolHTTP Protocol = "HTTP" ProtocolHTTPS Protocol = "HTTPS" // Protocol SCTP requires octavia microversion 2.23 - ProtocolSCTP Protocol = "SCTP" + ProtocolSCTP Protocol = "SCTP" + // Protocol Prometheus requires octavia microversion 2.25 + ProtocolPrometheus Protocol = "PROMETHEUS" ProtocolTerminatedHTTPS Protocol = "TERMINATED_HTTPS" ) From c741b60c1d310b4f717d0f6c6e1c6495cfae2cd3 Mon Sep 17 00:00:00 2001 From: nikParasyr <nik.parasyr@protonmail.com> Date: Wed, 12 Oct 2022 17:51:25 +0200 Subject: [PATCH 150/360] Add Persistance for octavia pools.UpdateOpts Session persistance can be updated on Octavia. Add it to UpdateOpts --- openstack/loadbalancer/v2/pools/requests.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/openstack/loadbalancer/v2/pools/requests.go b/openstack/loadbalancer/v2/pools/requests.go index 41d7dd9a41..69e6a2a763 100644 --- a/openstack/loadbalancer/v2/pools/requests.go +++ b/openstack/loadbalancer/v2/pools/requests.go @@ -190,6 +190,9 @@ type UpdateOpts struct { // or false (DOWN). AdminStateUp *bool `json:"admin_state_up,omitempty"` + // Persistence is the session persistence of the pool. + Persistence *SessionPersistence `json:"session_persistence,omitempty"` + // Tags is a set of resource tags. New in version 2.5 Tags *[]string `json:"tags,omitempty"` } From 1470f5c989d72a044fd9f3374df067f399c03b14 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 13 Oct 2022 09:45:41 +0000 Subject: [PATCH 151/360] Bump EmilienM/devstack-action from 0.7 to 0.8 Bumps [EmilienM/devstack-action](https://github.com/EmilienM/devstack-action) from 0.7 to 0.8. - [Release notes](https://github.com/EmilienM/devstack-action/releases) - [Commits](https://github.com/EmilienM/devstack-action/compare/v0.7...v0.8) --- updated-dependencies: - dependency-name: EmilienM/devstack-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> --- .github/workflows/functional-baremetal.yaml | 2 +- .github/workflows/functional-basic.yaml | 2 +- .github/workflows/functional-blockstorage.yaml | 2 +- .github/workflows/functional-clustering.yaml | 2 +- .github/workflows/functional-compute.yaml | 2 +- .github/workflows/functional-containerinfra.yaml | 2 +- .github/workflows/functional-dns.yaml | 2 +- .github/workflows/functional-identity.yaml | 2 +- .github/workflows/functional-imageservice.yaml | 2 +- .github/workflows/functional-keymanager.yaml | 2 +- .github/workflows/functional-loadbalancer.yaml | 2 +- .github/workflows/functional-messaging.yaml | 2 +- .github/workflows/functional-networking.yaml | 2 +- .github/workflows/functional-objectstorage.yaml | 2 +- .github/workflows/functional-orchestration.yaml | 2 +- .github/workflows/functional-placement.yaml | 2 +- .github/workflows/functional-sharedfilesystems.yaml | 2 +- 17 files changed, 17 insertions(+), 17 deletions(-) diff --git a/.github/workflows/functional-baremetal.yaml b/.github/workflows/functional-baremetal.yaml index a4a4038017..065c0a5147 100644 --- a/.github/workflows/functional-baremetal.yaml +++ b/.github/workflows/functional-baremetal.yaml @@ -38,7 +38,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.7 + uses: EmilienM/devstack-action@v0.8 with: branch: ${{ matrix.openstack_version }} conf_overrides: | diff --git a/.github/workflows/functional-basic.yaml b/.github/workflows/functional-basic.yaml index 78b0f9760b..97e3fe0feb 100644 --- a/.github/workflows/functional-basic.yaml +++ b/.github/workflows/functional-basic.yaml @@ -41,7 +41,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.7 + uses: EmilienM/devstack-action@v0.8 with: branch: ${{ matrix.openstack_version }} enabled_services: 's-account,s-container,s-object,s-proxy' diff --git a/.github/workflows/functional-blockstorage.yaml b/.github/workflows/functional-blockstorage.yaml index 6878da6d94..557e737c31 100644 --- a/.github/workflows/functional-blockstorage.yaml +++ b/.github/workflows/functional-blockstorage.yaml @@ -38,7 +38,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.7 + uses: EmilienM/devstack-action@v0.8 with: branch: ${{ matrix.openstack_version }} conf_overrides: | diff --git a/.github/workflows/functional-clustering.yaml b/.github/workflows/functional-clustering.yaml index 0c6247887b..128c4c14ca 100644 --- a/.github/workflows/functional-clustering.yaml +++ b/.github/workflows/functional-clustering.yaml @@ -38,7 +38,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.7 + uses: EmilienM/devstack-action@v0.8 with: branch: ${{ matrix.openstack_version }} conf_overrides: | diff --git a/.github/workflows/functional-compute.yaml b/.github/workflows/functional-compute.yaml index 8e9ad33b49..3f788fa53b 100644 --- a/.github/workflows/functional-compute.yaml +++ b/.github/workflows/functional-compute.yaml @@ -38,7 +38,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.7 + uses: EmilienM/devstack-action@v0.8 with: branch: ${{ matrix.openstack_version }} conf_overrides: | diff --git a/.github/workflows/functional-containerinfra.yaml b/.github/workflows/functional-containerinfra.yaml index 6cccd1cb3b..2f6b58f76e 100644 --- a/.github/workflows/functional-containerinfra.yaml +++ b/.github/workflows/functional-containerinfra.yaml @@ -38,7 +38,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.7 + uses: EmilienM/devstack-action@v0.8 with: branch: ${{ matrix.openstack_version }} conf_overrides: | diff --git a/.github/workflows/functional-dns.yaml b/.github/workflows/functional-dns.yaml index 6e9f6ea389..572fdc9129 100644 --- a/.github/workflows/functional-dns.yaml +++ b/.github/workflows/functional-dns.yaml @@ -39,7 +39,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.7 + uses: EmilienM/devstack-action@v0.8 with: branch: ${{ matrix.openstack_version }} conf_overrides: | diff --git a/.github/workflows/functional-identity.yaml b/.github/workflows/functional-identity.yaml index fcddd1db5a..97eca1b270 100644 --- a/.github/workflows/functional-identity.yaml +++ b/.github/workflows/functional-identity.yaml @@ -38,7 +38,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.7 + uses: EmilienM/devstack-action@v0.8 with: branch: ${{ matrix.openstack_version }} - name: Checkout go diff --git a/.github/workflows/functional-imageservice.yaml b/.github/workflows/functional-imageservice.yaml index 83a17356e1..ba57071d84 100644 --- a/.github/workflows/functional-imageservice.yaml +++ b/.github/workflows/functional-imageservice.yaml @@ -38,7 +38,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.7 + uses: EmilienM/devstack-action@v0.8 with: branch: ${{ matrix.openstack_version }} - name: Checkout go diff --git a/.github/workflows/functional-keymanager.yaml b/.github/workflows/functional-keymanager.yaml index 994aa3fe9b..25b9ed4568 100644 --- a/.github/workflows/functional-keymanager.yaml +++ b/.github/workflows/functional-keymanager.yaml @@ -38,7 +38,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.7 + uses: EmilienM/devstack-action@v0.8 with: branch: ${{ matrix.openstack_version }} conf_overrides: | diff --git a/.github/workflows/functional-loadbalancer.yaml b/.github/workflows/functional-loadbalancer.yaml index 827be255b1..690857d2be 100644 --- a/.github/workflows/functional-loadbalancer.yaml +++ b/.github/workflows/functional-loadbalancer.yaml @@ -38,7 +38,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.7 + uses: EmilienM/devstack-action@v0.8 with: branch: ${{ matrix.openstack_version }} conf_overrides: | diff --git a/.github/workflows/functional-messaging.yaml b/.github/workflows/functional-messaging.yaml index 6a8adf9fac..5e74b98038 100644 --- a/.github/workflows/functional-messaging.yaml +++ b/.github/workflows/functional-messaging.yaml @@ -38,7 +38,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.7 + uses: EmilienM/devstack-action@v0.8 with: branch: ${{ matrix.openstack_version }} conf_overrides: | diff --git a/.github/workflows/functional-networking.yaml b/.github/workflows/functional-networking.yaml index 315ed5a2ae..9de7888106 100644 --- a/.github/workflows/functional-networking.yaml +++ b/.github/workflows/functional-networking.yaml @@ -61,7 +61,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.7 + uses: EmilienM/devstack-action@v0.8 with: branch: ${{ matrix.openstack_version }} conf_overrides: | diff --git a/.github/workflows/functional-objectstorage.yaml b/.github/workflows/functional-objectstorage.yaml index 85e6107828..5c79372ac8 100644 --- a/.github/workflows/functional-objectstorage.yaml +++ b/.github/workflows/functional-objectstorage.yaml @@ -38,7 +38,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.7 + uses: EmilienM/devstack-action@v0.8 with: branch: ${{ matrix.openstack_version }} conf_overrides: | diff --git a/.github/workflows/functional-orchestration.yaml b/.github/workflows/functional-orchestration.yaml index 28d3b291a6..2605e170fe 100644 --- a/.github/workflows/functional-orchestration.yaml +++ b/.github/workflows/functional-orchestration.yaml @@ -38,7 +38,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.7 + uses: EmilienM/devstack-action@v0.8 with: branch: ${{ matrix.openstack_version }} conf_overrides: | diff --git a/.github/workflows/functional-placement.yaml b/.github/workflows/functional-placement.yaml index ea514716ff..784f2de6ce 100644 --- a/.github/workflows/functional-placement.yaml +++ b/.github/workflows/functional-placement.yaml @@ -38,7 +38,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.7 + uses: EmilienM/devstack-action@v0.8 with: branch: ${{ matrix.openstack_version }} - name: Checkout go diff --git a/.github/workflows/functional-sharedfilesystems.yaml b/.github/workflows/functional-sharedfilesystems.yaml index 7884bda683..47435f99d5 100644 --- a/.github/workflows/functional-sharedfilesystems.yaml +++ b/.github/workflows/functional-sharedfilesystems.yaml @@ -38,7 +38,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.7 + uses: EmilienM/devstack-action@v0.8 with: branch: ${{ matrix.openstack_version }} conf_overrides: | From 9e21d13fd164cfdaa9a866b8ea570da56dafb784 Mon Sep 17 00:00:00 2001 From: nikParasyr <nik.parasyr@protonmail.com> Date: Thu, 13 Oct 2022 15:25:27 +0200 Subject: [PATCH 152/360] Add VipQosPolicyID to loadbalancer Create and Update VipQosPolicyID can be defined for loadbalancers during creation and update. Moreover add the `neutron-qos` service to loadbalancer workflow. [Docs](https://docs.openstack.org/api-ref/load-balancer/v2/?expanded=create-a-load-balancer-detail,update-a-load-balancer-detail#create-a-load-balancer) --- .../workflows/functional-loadbalancer.yaml | 2 +- .../openstack/loadbalancer/v2/loadbalancer.go | 10 +++++++- .../loadbalancer/v2/loadbalancers_test.go | 24 +++++++++++++++---- .../loadbalancer/v2/loadbalancers/requests.go | 6 +++++ .../loadbalancer/v2/loadbalancers/results.go | 3 +++ 5 files changed, 38 insertions(+), 7 deletions(-) diff --git a/.github/workflows/functional-loadbalancer.yaml b/.github/workflows/functional-loadbalancer.yaml index 827be255b1..4fe92ea876 100644 --- a/.github/workflows/functional-loadbalancer.yaml +++ b/.github/workflows/functional-loadbalancer.yaml @@ -44,7 +44,7 @@ jobs: conf_overrides: | enable_plugin octavia https://opendev.org/openstack/octavia ${{ matrix.openstack_version }} enable_plugin neutron https://opendev.org/openstack/neutron ${{ matrix.openstack_version }} - enabled_services: 'octavia,o-api,o-cw,o-hk,o-hm,o-da' + enabled_services: 'octavia,o-api,o-cw,o-hk,o-hm,o-da,neutron-qos' - name: Checkout go uses: actions/setup-go@v3 with: diff --git a/acceptance/openstack/loadbalancer/v2/loadbalancer.go b/acceptance/openstack/loadbalancer/v2/loadbalancer.go index f8d44a8b17..8d5efb011d 100644 --- a/acceptance/openstack/loadbalancer/v2/loadbalancer.go +++ b/acceptance/openstack/loadbalancer/v2/loadbalancer.go @@ -110,7 +110,7 @@ func CreateListenerHTTP(t *testing.T, client *gophercloud.ServiceClient, lb *loa // CreateLoadBalancer will create a load balancer with a random name on a given // subnet. An error will be returned if the loadbalancer could not be created. -func CreateLoadBalancer(t *testing.T, client *gophercloud.ServiceClient, subnetID string, tags []string) (*loadbalancers.LoadBalancer, error) { +func CreateLoadBalancer(t *testing.T, client *gophercloud.ServiceClient, subnetID string, tags []string, policyID string) (*loadbalancers.LoadBalancer, error) { lbName := tools.RandomString("TESTACCT-", 8) lbDescription := tools.RandomString("TESTACCT-DESC-", 8) @@ -126,6 +126,10 @@ func CreateLoadBalancer(t *testing.T, client *gophercloud.ServiceClient, subnetI createOpts.Tags = tags } + if len(policyID) > 0 { + createOpts.VipQosPolicyID = policyID + } + lb, err := loadbalancers.Create(client, createOpts).Extract() if err != nil { return lb, err @@ -149,6 +153,10 @@ func CreateLoadBalancer(t *testing.T, client *gophercloud.ServiceClient, subnetI th.AssertDeepEquals(t, lb.Tags, tags) } + if len(policyID) > 0 { + th.AssertEquals(t, lb.VipQosPolicyID, policyID) + } + return lb, nil } diff --git a/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go b/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go index c21663ef35..2a987bd9b9 100644 --- a/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go +++ b/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go @@ -8,6 +8,7 @@ import ( "github.com/gophercloud/gophercloud/acceptance/clients" networking "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2" + "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2/extensions/qos/policies" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/l7policies" "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/listeners" @@ -50,7 +51,7 @@ func TestLoadbalancersListByTags(t *testing.T) { // Add "test" tag intentionally to test the "not-tags" parameter. Because "test" tag is also used in other test // cases, we use "test" tag to exclude load balancers created by other test case. tags := []string{"tag1", "tag2", "test"} - lb, err := CreateLoadBalancer(t, lbClient, subnet.ID, tags) + lb, err := CreateLoadBalancer(t, lbClient, subnet.ID, tags, "") th.AssertNoErr(t, err) defer DeleteLoadBalancer(t, lbClient, lb.ID) @@ -110,7 +111,7 @@ func TestLoadbalancerHTTPCRUD(t *testing.T) { th.AssertNoErr(t, err) defer networking.DeleteSubnet(t, netClient, subnet.ID) - lb, err := CreateLoadBalancer(t, lbClient, subnet.ID, nil) + lb, err := CreateLoadBalancer(t, lbClient, subnet.ID, nil, "") th.AssertNoErr(t, err) defer DeleteLoadBalancer(t, lbClient, lb.ID) @@ -239,6 +240,12 @@ func TestLoadbalancersCRUD(t *testing.T) { netClient, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) + // Create QoS policy first as the loadbalancer and its port + //needs to be deleted before the QoS policy can be deleted + policy2, err := policies.CreateQoSPolicy(t, netClient) + th.AssertNoErr(t, err) + defer policies.DeleteQoSPolicy(t, netClient, policy2.ID) + lbClient, err := clients.NewLoadBalancerV2Client() th.AssertNoErr(t, err) @@ -250,14 +257,20 @@ func TestLoadbalancersCRUD(t *testing.T) { th.AssertNoErr(t, err) defer networking.DeleteSubnet(t, netClient, subnet.ID) + policy1, err := policies.CreateQoSPolicy(t, netClient) + th.AssertNoErr(t, err) + defer policies.DeleteQoSPolicy(t, netClient, policy1.ID) + tags := []string{"test"} - lb, err := CreateLoadBalancer(t, lbClient, subnet.ID, tags) + lb, err := CreateLoadBalancer(t, lbClient, subnet.ID, tags, policy1.ID) th.AssertNoErr(t, err) + th.AssertEquals(t, lb.VipQosPolicyID, policy1.ID) defer DeleteLoadBalancer(t, lbClient, lb.ID) lbDescription := "" updateLoadBalancerOpts := loadbalancers.UpdateOpts{ - Description: &lbDescription, + Description: &lbDescription, + VipQosPolicyID: &policy2.ID, } _, err = loadbalancers.Update(lbClient, lb.ID, updateLoadBalancerOpts).Extract() th.AssertNoErr(t, err) @@ -272,6 +285,7 @@ func TestLoadbalancersCRUD(t *testing.T) { tools.PrintResource(t, newLB) th.AssertEquals(t, newLB.Description, lbDescription) + th.AssertEquals(t, newLB.VipQosPolicyID, policy2.ID) lbStats, err := loadbalancers.GetStats(lbClient, lb.ID).Extract() th.AssertNoErr(t, err) @@ -449,7 +463,7 @@ func TestLoadbalancersCascadeCRUD(t *testing.T) { defer networking.DeleteSubnet(t, netClient, subnet.ID) tags := []string{"test"} - lb, err := CreateLoadBalancer(t, lbClient, subnet.ID, tags) + lb, err := CreateLoadBalancer(t, lbClient, subnet.ID, tags, "") th.AssertNoErr(t, err) defer CascadeDeleteLoadBalancer(t, lbClient, lb.ID) diff --git a/openstack/loadbalancer/v2/loadbalancers/requests.go b/openstack/loadbalancer/v2/loadbalancers/requests.go index 42179ce7e3..099113c418 100644 --- a/openstack/loadbalancer/v2/loadbalancers/requests.go +++ b/openstack/loadbalancer/v2/loadbalancers/requests.go @@ -107,6 +107,9 @@ type CreateOpts struct { // The IP address of the Loadbalancer. VipAddress string `json:"vip_address,omitempty"` + // The ID of the QoS Policy which will apply to the Virtual IP + VipQosPolicyID string `json:"vip_qos_policy_id,omitempty"` + // The administrative state of the Loadbalancer. A valid value is true (UP) // or false (DOWN). AdminStateUp *bool `json:"admin_state_up,omitempty"` @@ -185,6 +188,9 @@ type UpdateOpts struct { // or false (DOWN). AdminStateUp *bool `json:"admin_state_up,omitempty"` + // The ID of the QoS Policy which will apply to the Virtual IP + VipQosPolicyID *string `json:"vip_qos_policy_id,omitempty"` + // Tags is a set of resource tags. Tags *[]string `json:"tags,omitempty"` } diff --git a/openstack/loadbalancer/v2/loadbalancers/results.go b/openstack/loadbalancer/v2/loadbalancers/results.go index 9a385363f2..739337c4d6 100644 --- a/openstack/loadbalancer/v2/loadbalancers/results.go +++ b/openstack/loadbalancer/v2/loadbalancers/results.go @@ -47,6 +47,9 @@ type LoadBalancer struct { // Loadbalancer address. VipNetworkID string `json:"vip_network_id"` + // The ID of the QoS Policy which will apply to the Virtual IP + VipQosPolicyID string `json:"vip_qos_policy_id"` + // The unique ID for the LoadBalancer. ID string `json:"id"` From 2169d6bf20b3e7965d3dc3e2f9a0e5e1979f3b4d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 14 Oct 2022 09:29:32 +0000 Subject: [PATCH 153/360] Bump EmilienM/devstack-action from 0.8 to 0.9 Bumps [EmilienM/devstack-action](https://github.com/EmilienM/devstack-action) from 0.8 to 0.9. - [Release notes](https://github.com/EmilienM/devstack-action/releases) - [Commits](https://github.com/EmilienM/devstack-action/compare/v0.8...v0.9) --- updated-dependencies: - dependency-name: EmilienM/devstack-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> --- .github/workflows/functional-baremetal.yaml | 2 +- .github/workflows/functional-basic.yaml | 2 +- .github/workflows/functional-blockstorage.yaml | 2 +- .github/workflows/functional-clustering.yaml | 2 +- .github/workflows/functional-compute.yaml | 2 +- .github/workflows/functional-containerinfra.yaml | 2 +- .github/workflows/functional-dns.yaml | 2 +- .github/workflows/functional-identity.yaml | 2 +- .github/workflows/functional-imageservice.yaml | 2 +- .github/workflows/functional-keymanager.yaml | 2 +- .github/workflows/functional-loadbalancer.yaml | 2 +- .github/workflows/functional-messaging.yaml | 2 +- .github/workflows/functional-networking.yaml | 2 +- .github/workflows/functional-objectstorage.yaml | 2 +- .github/workflows/functional-orchestration.yaml | 2 +- .github/workflows/functional-placement.yaml | 2 +- .github/workflows/functional-sharedfilesystems.yaml | 2 +- 17 files changed, 17 insertions(+), 17 deletions(-) diff --git a/.github/workflows/functional-baremetal.yaml b/.github/workflows/functional-baremetal.yaml index 065c0a5147..f9f0925d75 100644 --- a/.github/workflows/functional-baremetal.yaml +++ b/.github/workflows/functional-baremetal.yaml @@ -38,7 +38,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.8 + uses: EmilienM/devstack-action@v0.9 with: branch: ${{ matrix.openstack_version }} conf_overrides: | diff --git a/.github/workflows/functional-basic.yaml b/.github/workflows/functional-basic.yaml index 97e3fe0feb..778f7bcad2 100644 --- a/.github/workflows/functional-basic.yaml +++ b/.github/workflows/functional-basic.yaml @@ -41,7 +41,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.8 + uses: EmilienM/devstack-action@v0.9 with: branch: ${{ matrix.openstack_version }} enabled_services: 's-account,s-container,s-object,s-proxy' diff --git a/.github/workflows/functional-blockstorage.yaml b/.github/workflows/functional-blockstorage.yaml index 557e737c31..8baed7dc45 100644 --- a/.github/workflows/functional-blockstorage.yaml +++ b/.github/workflows/functional-blockstorage.yaml @@ -38,7 +38,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.8 + uses: EmilienM/devstack-action@v0.9 with: branch: ${{ matrix.openstack_version }} conf_overrides: | diff --git a/.github/workflows/functional-clustering.yaml b/.github/workflows/functional-clustering.yaml index 128c4c14ca..9bd52ee967 100644 --- a/.github/workflows/functional-clustering.yaml +++ b/.github/workflows/functional-clustering.yaml @@ -38,7 +38,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.8 + uses: EmilienM/devstack-action@v0.9 with: branch: ${{ matrix.openstack_version }} conf_overrides: | diff --git a/.github/workflows/functional-compute.yaml b/.github/workflows/functional-compute.yaml index 3f788fa53b..f15786a2b0 100644 --- a/.github/workflows/functional-compute.yaml +++ b/.github/workflows/functional-compute.yaml @@ -38,7 +38,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.8 + uses: EmilienM/devstack-action@v0.9 with: branch: ${{ matrix.openstack_version }} conf_overrides: | diff --git a/.github/workflows/functional-containerinfra.yaml b/.github/workflows/functional-containerinfra.yaml index 2f6b58f76e..8e320a1ff5 100644 --- a/.github/workflows/functional-containerinfra.yaml +++ b/.github/workflows/functional-containerinfra.yaml @@ -38,7 +38,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.8 + uses: EmilienM/devstack-action@v0.9 with: branch: ${{ matrix.openstack_version }} conf_overrides: | diff --git a/.github/workflows/functional-dns.yaml b/.github/workflows/functional-dns.yaml index 572fdc9129..782c9e05d4 100644 --- a/.github/workflows/functional-dns.yaml +++ b/.github/workflows/functional-dns.yaml @@ -39,7 +39,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.8 + uses: EmilienM/devstack-action@v0.9 with: branch: ${{ matrix.openstack_version }} conf_overrides: | diff --git a/.github/workflows/functional-identity.yaml b/.github/workflows/functional-identity.yaml index 97eca1b270..58b7c08434 100644 --- a/.github/workflows/functional-identity.yaml +++ b/.github/workflows/functional-identity.yaml @@ -38,7 +38,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.8 + uses: EmilienM/devstack-action@v0.9 with: branch: ${{ matrix.openstack_version }} - name: Checkout go diff --git a/.github/workflows/functional-imageservice.yaml b/.github/workflows/functional-imageservice.yaml index ba57071d84..5059267da8 100644 --- a/.github/workflows/functional-imageservice.yaml +++ b/.github/workflows/functional-imageservice.yaml @@ -38,7 +38,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.8 + uses: EmilienM/devstack-action@v0.9 with: branch: ${{ matrix.openstack_version }} - name: Checkout go diff --git a/.github/workflows/functional-keymanager.yaml b/.github/workflows/functional-keymanager.yaml index 25b9ed4568..53f0f0e15f 100644 --- a/.github/workflows/functional-keymanager.yaml +++ b/.github/workflows/functional-keymanager.yaml @@ -38,7 +38,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.8 + uses: EmilienM/devstack-action@v0.9 with: branch: ${{ matrix.openstack_version }} conf_overrides: | diff --git a/.github/workflows/functional-loadbalancer.yaml b/.github/workflows/functional-loadbalancer.yaml index 15dafad29d..e73fb303b7 100644 --- a/.github/workflows/functional-loadbalancer.yaml +++ b/.github/workflows/functional-loadbalancer.yaml @@ -38,7 +38,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.8 + uses: EmilienM/devstack-action@v0.9 with: branch: ${{ matrix.openstack_version }} conf_overrides: | diff --git a/.github/workflows/functional-messaging.yaml b/.github/workflows/functional-messaging.yaml index 5e74b98038..8215fa4c38 100644 --- a/.github/workflows/functional-messaging.yaml +++ b/.github/workflows/functional-messaging.yaml @@ -38,7 +38,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.8 + uses: EmilienM/devstack-action@v0.9 with: branch: ${{ matrix.openstack_version }} conf_overrides: | diff --git a/.github/workflows/functional-networking.yaml b/.github/workflows/functional-networking.yaml index 9de7888106..dd1ccd7ff5 100644 --- a/.github/workflows/functional-networking.yaml +++ b/.github/workflows/functional-networking.yaml @@ -61,7 +61,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.8 + uses: EmilienM/devstack-action@v0.9 with: branch: ${{ matrix.openstack_version }} conf_overrides: | diff --git a/.github/workflows/functional-objectstorage.yaml b/.github/workflows/functional-objectstorage.yaml index 5c79372ac8..f87be4b502 100644 --- a/.github/workflows/functional-objectstorage.yaml +++ b/.github/workflows/functional-objectstorage.yaml @@ -38,7 +38,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.8 + uses: EmilienM/devstack-action@v0.9 with: branch: ${{ matrix.openstack_version }} conf_overrides: | diff --git a/.github/workflows/functional-orchestration.yaml b/.github/workflows/functional-orchestration.yaml index 2605e170fe..f3030bf2db 100644 --- a/.github/workflows/functional-orchestration.yaml +++ b/.github/workflows/functional-orchestration.yaml @@ -38,7 +38,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.8 + uses: EmilienM/devstack-action@v0.9 with: branch: ${{ matrix.openstack_version }} conf_overrides: | diff --git a/.github/workflows/functional-placement.yaml b/.github/workflows/functional-placement.yaml index 784f2de6ce..5f6ba1fcb7 100644 --- a/.github/workflows/functional-placement.yaml +++ b/.github/workflows/functional-placement.yaml @@ -38,7 +38,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.8 + uses: EmilienM/devstack-action@v0.9 with: branch: ${{ matrix.openstack_version }} - name: Checkout go diff --git a/.github/workflows/functional-sharedfilesystems.yaml b/.github/workflows/functional-sharedfilesystems.yaml index 47435f99d5..83c9d97519 100644 --- a/.github/workflows/functional-sharedfilesystems.yaml +++ b/.github/workflows/functional-sharedfilesystems.yaml @@ -38,7 +38,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.8 + uses: EmilienM/devstack-action@v0.9 with: branch: ${{ matrix.openstack_version }} conf_overrides: | From 32266607e09a4ae3f5da2a7d5806f630fc9fee2d Mon Sep 17 00:00:00 2001 From: Stas Kraev <staskraev@catalyst.net.nz> Date: Mon, 31 Oct 2022 20:55:52 +1300 Subject: [PATCH 154/360] Add support for l3-agent-scheduler extensions --- .../v2/extensions/agents/requests.go | 46 +++++++++ .../v2/extensions/agents/results.go | 31 ++++++ .../v2/extensions/agents/testing/fixtures.go | 86 ++++++++++++++++ .../agents/testing/requests_test.go | 98 +++++++++++++++++++ .../networking/v2/extensions/agents/urls.go | 19 ++++ 5 files changed, 280 insertions(+) diff --git a/openstack/networking/v2/extensions/agents/requests.go b/openstack/networking/v2/extensions/agents/requests.go index 707f710c4e..5a3c4c35c3 100644 --- a/openstack/networking/v2/extensions/agents/requests.go +++ b/openstack/networking/v2/extensions/agents/requests.go @@ -205,3 +205,49 @@ func ListDRAgentHostingBGPSpeakers(c *gophercloud.ServiceClient, bgpSpeakerID st return AgentPage{pagination.LinkedPageBase{PageResult: r}} }) } + +// ListL3Routers returns a list of routers scheduled to a specific +// L3 agent. +func ListL3Routers(c *gophercloud.ServiceClient, id string) (r ListL3RoutersResult) { + resp, err := c.Get(listL3RoutersURL(c, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// ScheduleL3RouterOptsBuilder allows extensions to add additional parameters +// to the ScheduleL3Router request. +type ScheduleL3RouterOptsBuilder interface { + ToAgentScheduleL3RouterMap() (map[string]interface{}, error) +} + +// ScheduleL3RouterOpts represents the attributes used when scheduling a +// router to a L3 agent. +type ScheduleL3RouterOpts struct { + RouterID string `json:"router_id" required:"true"` +} + +// ToAgentScheduleL3RouterMap builds a request body from ScheduleL3RouterOpts. +func (opts ScheduleL3RouterOpts) ToAgentScheduleL3RouterMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "") +} + +// ScheduleL3Router schedule a router to a L3 agent. +func ScheduleL3Router(c *gophercloud.ServiceClient, id string, opts ScheduleL3RouterOptsBuilder) (r ScheduleL3RouterResult) { + b, err := opts.ToAgentScheduleL3RouterMap() + if err != nil { + r.Err = err + return + } + resp, err := c.Post(scheduleL3RouterURL(c, id), b, nil, &gophercloud.RequestOpts{ + OkCodes: []int{201}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// RemoveL3Router removes a router from a L3 agent. +func RemoveL3Router(c *gophercloud.ServiceClient, id string, routerID string) (r RemoveL3RouterResult) { + resp, err := c.Delete(removeL3RouterURL(c, id, routerID), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} diff --git a/openstack/networking/v2/extensions/agents/results.go b/openstack/networking/v2/extensions/agents/results.go index 5f66eb37fe..0af9e0fdd0 100644 --- a/openstack/networking/v2/extensions/agents/results.go +++ b/openstack/networking/v2/extensions/agents/results.go @@ -6,6 +6,7 @@ import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/bgp/speakers" + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/routers" "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" "github.com/gophercloud/gophercloud/pagination" ) @@ -208,3 +209,33 @@ func ExtractBGPSpeakers(r pagination.Page) ([]speakers.BGPSpeaker, error) { err := (r.(ListBGPSpeakersResult)).ExtractInto(&s) return s.Speakers, err } + +// ListL3RoutersResult is the response from a List operation. +// Call its Extract method to interpret it as routers. +type ListL3RoutersResult struct { + gophercloud.Result +} + +// ScheduleL3RouterResult represents the result of a schedule a router to +// a L3 agent operation. ExtractErr method to determine if the request +// succeeded or failed. +type ScheduleL3RouterResult struct { + gophercloud.ErrResult +} + +// RemoveL3RouterResult represents the result of a remove a router from a +// L3 agent operation. ExtractErr method to determine if the request succeeded +// or failed. +type RemoveL3RouterResult struct { + gophercloud.ErrResult +} + +// Extract interprets any ListL3RoutesResult as an array of routers. +func (r ListL3RoutersResult) Extract() ([]routers.Router, error) { + var s struct { + Routers []routers.Router `json:"routers"` + } + + err := r.ExtractInto(&s) + return s.Routers, err +} diff --git a/openstack/networking/v2/extensions/agents/testing/fixtures.go b/openstack/networking/v2/extensions/agents/testing/fixtures.go index 35b4ca5232..d40bc6ecdb 100644 --- a/openstack/networking/v2/extensions/agents/testing/fixtures.go +++ b/openstack/networking/v2/extensions/agents/testing/fixtures.go @@ -345,3 +345,89 @@ const ListDRAgentHostingBGPSpeakersResult = ` ] } ` + +// AgentL3ListListResult represents raw response for the ListL3Routers request. +const AgentL3RoutersListResult = ` +{ + "routers": [ + { + "admin_state_up": true, + "availability_zone_hints": [], + "availability_zones": [ + "nova" + ], + "description": "", + "distributed": false, + "external_gateway_info": { + "enable_snat": true, + "external_fixed_ips": [ + { + "ip_address": "172.24.4.3", + "subnet_id": "b930d7f6-ceb7-40a0-8b81-a425dd994ccf" + }, + { + "ip_address": "2001:db8::c", + "subnet_id": "0c56df5d-ace5-46c8-8f4c-45fa4e334d18" + } + ], + "network_id": "ae34051f-aa6c-4c75-abf5-50dc9ac99ef3" + }, + "flavor_id": "f7b14d9a-b0dc-4fbe-bb14-a0f4970a69e0", + "ha": false, + "id": "915a14a6-867b-4af7-83d1-70efceb146f9", + "name": "router2", + "revision_number": 1, + "routes": [ + { + "destination": "179.24.1.0/24", + "nexthop": "172.24.3.99" + } + ], + "status": "ACTIVE", + "project_id": "0bd18306d801447bb457a46252d82d13", + "tenant_id": "0bd18306d801447bb457a46252d82d13", + "service_type_id": null + }, + { + "admin_state_up": true, + "availability_zone_hints": [], + "availability_zones": [ + "nova" + ], + "description": "", + "distributed": false, + "external_gateway_info": { + "enable_snat": true, + "external_fixed_ips": [ + { + "ip_address": "172.24.4.6", + "subnet_id": "b930d7f6-ceb7-40a0-8b81-a425dd994ccf" + }, + { + "ip_address": "2001:db8::9", + "subnet_id": "0c56df5d-ace5-46c8-8f4c-45fa4e334d18" + } + ], + "network_id": "ae34051f-aa6c-4c75-abf5-50dc9ac99ef3" + }, + "flavor_id": "f7b14d9a-b0dc-4fbe-bb14-a0f4970a69e0", + "ha": false, + "id": "f8a44de0-fc8e-45df-93c7-f79bf3b01c95", + "name": "router1", + "revision_number": 1, + "routes": [], + "status": "ACTIVE", + "project_id": "0bd18306d801447bb457a46252d82d13", + "tenant_id": "0bd18306d801447bb457a46252d82d13", + "service_type_id": null + } + ] +} +` + +// ScheduleL3RouterRequest represents raw request for the ScheduleL3Router request. +const ScheduleL3RouterRequest = ` +{ + "router_id": "43e66290-79a4-415d-9eb9-7ff7919839e1" +} +` diff --git a/openstack/networking/v2/extensions/agents/testing/requests_test.go b/openstack/networking/v2/extensions/agents/testing/requests_test.go index 10411b5910..296e8a2cdf 100644 --- a/openstack/networking/v2/extensions/agents/testing/requests_test.go +++ b/openstack/networking/v2/extensions/agents/testing/requests_test.go @@ -8,6 +8,7 @@ import ( fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/agents" + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/routers" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" ) @@ -323,3 +324,100 @@ func TestListDRAgentHostingBGPSpeakers(t *testing.T) { t.Errorf("Expected 1 page, got %d", count) } } + +func TestListL3Routers(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/agents/43583cf5-472e-4dc8-af5b-6aed4c94ee3a/l3-routers", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, AgentL3RoutersListResult) + }) + + s, err := agents.ListL3Routers(fake.ServiceClient(), "43583cf5-472e-4dc8-af5b-6aed4c94ee3a").Extract() + th.AssertNoErr(t, err) + + routes := []routers.Route{ + { + "172.24.3.99", + "179.24.1.0/24", + }, + } + + var snat bool = true + gw := routers.GatewayInfo{ + EnableSNAT: &snat, + NetworkID: "ae34051f-aa6c-4c75-abf5-50dc9ac99ef3", + ExternalFixedIPs: []routers.ExternalFixedIP{ + { + IPAddress: "172.24.4.3", + SubnetID: "b930d7f6-ceb7-40a0-8b81-a425dd994ccf", + }, + + { + IPAddress: "2001:db8::c", + SubnetID: "0c56df5d-ace5-46c8-8f4c-45fa4e334d18", + }, + }, + } + + var nilSlice []string + th.AssertEquals(t, len(s), 2) + th.AssertEquals(t, s[0].ID, "915a14a6-867b-4af7-83d1-70efceb146f9") + th.AssertEquals(t, s[0].AdminStateUp, true) + th.AssertEquals(t, s[0].ProjectID, "0bd18306d801447bb457a46252d82d13") + th.AssertEquals(t, s[0].Name, "router2") + th.AssertEquals(t, s[0].Status, "ACTIVE") + th.AssertEquals(t, s[0].TenantID, "0bd18306d801447bb457a46252d82d13") + th.AssertDeepEquals(t, s[0].AvailabilityZoneHints, []string{}) + th.AssertDeepEquals(t, s[0].Routes, routes) + th.AssertDeepEquals(t, s[0].GatewayInfo, gw) + th.AssertDeepEquals(t, s[0].Tags, nilSlice) + th.AssertEquals(t, s[1].ID, "f8a44de0-fc8e-45df-93c7-f79bf3b01c95") + th.AssertEquals(t, s[1].Name, "router1") + +} + +func TestScheduleL3Router(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/agents/43583cf5-472e-4dc8-af5b-6aed4c94ee3a/l3-routers", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestHeader(t, r, "Content-Type", "application/json") + th.TestHeader(t, r, "Accept", "application/json") + th.TestJSONRequest(t, r, ScheduleL3RouterRequest) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusCreated) + }) + + opts := &agents.ScheduleL3RouterOpts{ + RouterID: "43e66290-79a4-415d-9eb9-7ff7919839e1", + } + err := agents.ScheduleL3Router(fake.ServiceClient(), "43583cf5-472e-4dc8-af5b-6aed4c94ee3a", opts).ExtractErr() + th.AssertNoErr(t, err) +} + +func TestRemoveL3Router(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/agents/43583cf5-472e-4dc8-af5b-6aed4c94ee3a/l3-routers/43e66290-79a4-415d-9eb9-7ff7919839e1", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "DELETE") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestHeader(t, r, "Accept", "application/json") + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusNoContent) + }) + + err := agents.RemoveL3Router(fake.ServiceClient(), "43583cf5-472e-4dc8-af5b-6aed4c94ee3a", "43e66290-79a4-415d-9eb9-7ff7919839e1").ExtractErr() + th.AssertNoErr(t, err) +} diff --git a/openstack/networking/v2/extensions/agents/urls.go b/openstack/networking/v2/extensions/agents/urls.go index f6021fce2d..d4581ea3e7 100644 --- a/openstack/networking/v2/extensions/agents/urls.go +++ b/openstack/networking/v2/extensions/agents/urls.go @@ -4,6 +4,7 @@ import "github.com/gophercloud/gophercloud" const resourcePath = "agents" const dhcpNetworksResourcePath = "dhcp-networks" +const l3RoutersResourcePath = "l3-routers" const bgpSpeakersResourcePath = "bgp-drinstances" const bgpDRAgentSpeakersResourcePath = "bgp-speakers" const bgpDRAgentAgentResourcePath = "bgp-dragents" @@ -36,18 +37,36 @@ func dhcpNetworksURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL(resourcePath, id, dhcpNetworksResourcePath) } +func l3RoutersURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL(resourcePath, id, l3RoutersResourcePath) +} + func listDHCPNetworksURL(c *gophercloud.ServiceClient, id string) string { return dhcpNetworksURL(c, id) } +func listL3RoutersURL(c *gophercloud.ServiceClient, id string) string { + // TODO + // hmm list should be the plain l3RoutersURL but dhcp example tell otherwise + return l3RoutersURL(c, id) +} + func scheduleDHCPNetworkURL(c *gophercloud.ServiceClient, id string) string { return dhcpNetworksURL(c, id) } +func scheduleL3RouterURL(c *gophercloud.ServiceClient, id string) string { + return l3RoutersURL(c, id) +} + func removeDHCPNetworkURL(c *gophercloud.ServiceClient, id string, networkID string) string { return c.ServiceURL(resourcePath, id, dhcpNetworksResourcePath, networkID) } +func removeL3RouterURL(c *gophercloud.ServiceClient, id string, routerID string) string { + return c.ServiceURL(resourcePath, id, l3RoutersResourcePath, routerID) +} + // return /v2.0/agents/{agent-id}/bgp-drinstances func listBGPSpeakersURL(c *gophercloud.ServiceClient, agentID string) string { return c.ServiceURL(resourcePath, agentID, bgpSpeakersResourcePath) From effefce7d06135d7c4b80cbc9948ef81adc615a6 Mon Sep 17 00:00:00 2001 From: Stas Kraev <staskraev@catalyst.net.nz> Date: Tue, 1 Nov 2022 12:20:27 +1300 Subject: [PATCH 155/360] Add documentation for l3-agent-scheduler --- .../networking/v2/extensions/agents/doc.go | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/openstack/networking/v2/extensions/agents/doc.go b/openstack/networking/v2/extensions/agents/doc.go index 59eb233bd4..a52c709781 100644 --- a/openstack/networking/v2/extensions/agents/doc.go +++ b/openstack/networking/v2/extensions/agents/doc.go @@ -126,6 +126,37 @@ Example to list dragents hosting specific bgp speaker for _, a := range allAgents { log.Printf("%+v", a) } + +Example to list routers scheduled to L3 agent + + routers, err := agents.ListL3Routers(neutron, "655967f5-d6f3-4732-88f5-617b0ff5c356").Extract() + if err != nil { + log.Panic(err) + } + + for _, r := range routers { + log.Printf("%+v", r) + } + +Example to remove router from L3 agent + + agentID := "0e1095ae-6f36-40f3-8322-8e1c9a5e68ca" + routerID := "e6fa0457-efc2-491d-ac12-17ab60417efd" + err = agents.RemoveL3Router(neutron, "0e1095ae-6f36-40f3-8322-8e1c9a5e68ca", "e6fa0457-efc2-491d-ac12-17ab60417efd").ExtractErr() + if err != nil { + log.Panic(err) + } + +Example to schedule router to L3 agent + + agentID := "0e1095ae-6f36-40f3-8322-8e1c9a5e68ca" + routerID := "e6fa0457-efc2-491d-ac12-17ab60417efd" + err = agents.ScheduleL3Router(neutron, agentID, agents.ScheduleL3RouterOpts{routerID}).ExtractErr() + if err != nil { + log.Panic(err) + } + + */ package agents From a795059f3b5d7cd9a0c388840682355d00858259 Mon Sep 17 00:00:00 2001 From: Stas Kraev <staskraev@catalyst.net.nz> Date: Tue, 1 Nov 2022 20:59:35 +1300 Subject: [PATCH 156/360] Add acceptance test for l3-agent-scheduler --- .../extensions/layer3/l3_scheduling_test.go | 76 +++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 acceptance/openstack/networking/v2/extensions/layer3/l3_scheduling_test.go diff --git a/acceptance/openstack/networking/v2/extensions/layer3/l3_scheduling_test.go b/acceptance/openstack/networking/v2/extensions/layer3/l3_scheduling_test.go new file mode 100644 index 0000000000..7cc679bc83 --- /dev/null +++ b/acceptance/openstack/networking/v2/extensions/layer3/l3_scheduling_test.go @@ -0,0 +1,76 @@ +//go:build acceptance || networking || layer3 || router +// +build acceptance networking layer3 router + +package layer3 + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + networking "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/agents" + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/routers" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestLayer3RouterScheduling(t *testing.T) { + client, err := clients.NewNetworkV2Client() + th.AssertNoErr(t, err) + + network, err := networking.CreateNetwork(t, client) + th.AssertNoErr(t, err) + defer networking.DeleteNetwork(t, client, network.ID) + + subnet, err := networking.CreateSubnet(t, client, network.ID) + th.AssertNoErr(t, err) + defer networking.DeleteSubnet(t, client, subnet.ID) + + router, err := CreateRouter(t, client, network.ID) + th.AssertNoErr(t, err) + defer DeleteRouter(t, client, router.ID) + tools.PrintResource(t, router) + + routerInterface, err := CreateRouterInterfaceOnSubnet(t, client, subnet.ID, router.ID) + tools.PrintResource(t, routerInterface) + th.AssertNoErr(t, err) + defer DeleteRouterInterface(t, client, routerInterface.PortID, router.ID) + + // List hosting agent + allPages, err := routers.ListL3Agents(client, router.ID).AllPages() + th.AssertNoErr(t, err) + hostingAgents, err := routers.ExtractL3Agents(allPages) + th.AssertNoErr(t, err) + th.AssertEquals(t, len(hostingAgents) > 0, true) + hostingAgent := hostingAgents[0] + t.Logf("Router %s is scheduled on %s", router.ID, hostingAgent.ID) + + // remove from hosting agent + err = agents.RemoveL3Router(client, hostingAgent.ID, router.ID).ExtractErr() + th.AssertNoErr(t, err) + + containsRouterFunc := func(rs []routers.Router, routerID string) bool { + for _, r := range rs { + if r.ID == router.ID { + return true + } + } + return false + } + + // List routers on hosting agent + routersOnHostingAgent, err := agents.ListL3Routers(client, hostingAgent.ID).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, containsRouterFunc(routersOnHostingAgent, router.ID), false) + t.Logf("Router %s is not scheduled on %s", router.ID, hostingAgent.ID) + + // schedule back + err = agents.ScheduleL3Router(client, hostingAgents[0].ID, agents.ScheduleL3RouterOpts{RouterID: router.ID}).ExtractErr() + th.AssertNoErr(t, err) + + // List hosting agent after readding + routersOnHostingAgent, err = agents.ListL3Routers(client, hostingAgent.ID).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, containsRouterFunc(routersOnHostingAgent, router.ID), true) + t.Logf("Router %s is scheduled on %s", router.ID, hostingAgent.ID) +} From c5d5056080ec302e2a996f771c0a51195ff517b3 Mon Sep 17 00:00:00 2001 From: Stas Kraev <staskraev@catalyst.net.nz> Date: Tue, 1 Nov 2022 23:23:16 +1300 Subject: [PATCH 157/360] Fix go vet in neutron tests --- .../networking/v2/extensions/agents/testing/requests_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openstack/networking/v2/extensions/agents/testing/requests_test.go b/openstack/networking/v2/extensions/agents/testing/requests_test.go index 296e8a2cdf..d1afbc5c98 100644 --- a/openstack/networking/v2/extensions/agents/testing/requests_test.go +++ b/openstack/networking/v2/extensions/agents/testing/requests_test.go @@ -344,8 +344,8 @@ func TestListL3Routers(t *testing.T) { routes := []routers.Route{ { - "172.24.3.99", - "179.24.1.0/24", + NextHop: "172.24.3.99", + DestinationCIDR: "179.24.1.0/24", }, } From 1a3982feca40f4c5313fe46f8310a7a8c06d6aa3 Mon Sep 17 00:00:00 2001 From: Stas Kraev <staskraev@catalyst.net.nz> Date: Tue, 1 Nov 2022 23:35:54 +1300 Subject: [PATCH 158/360] Skip l3_scheduling acceptance test when extension not enabled --- .../networking/v2/extensions/layer3/l3_scheduling_test.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/acceptance/openstack/networking/v2/extensions/layer3/l3_scheduling_test.go b/acceptance/openstack/networking/v2/extensions/layer3/l3_scheduling_test.go index 7cc679bc83..905246af18 100644 --- a/acceptance/openstack/networking/v2/extensions/layer3/l3_scheduling_test.go +++ b/acceptance/openstack/networking/v2/extensions/layer3/l3_scheduling_test.go @@ -9,6 +9,7 @@ import ( "github.com/gophercloud/gophercloud/acceptance/clients" networking "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2" "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/common/extensions" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/agents" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/routers" th "github.com/gophercloud/gophercloud/testhelper" @@ -18,6 +19,11 @@ func TestLayer3RouterScheduling(t *testing.T) { client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) + _, err = extensions.Get(client, "l3_agent_scheduler").Extract() + if err != nil { + t.Skip("Extension l3_agent_scheduler not present") + } + network, err := networking.CreateNetwork(t, client) th.AssertNoErr(t, err) defer networking.DeleteNetwork(t, client, network.ID) From 9b25f1c6cc05f1da1f33f97dbbc9379abee62f17 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 3 Nov 2022 09:06:30 +0000 Subject: [PATCH 159/360] Bump actions/checkout from 2 to 3 Bumps [actions/checkout](https://github.com/actions/checkout) from 2 to 3. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v2...v3) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> --- .github/workflows/semver-labels.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/semver-labels.yaml b/.github/workflows/semver-labels.yaml index e6644a7bd7..ccaf44522b 100644 --- a/.github/workflows/semver-labels.yaml +++ b/.github/workflows/semver-labels.yaml @@ -10,7 +10,7 @@ jobs: semver: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: micnncim/action-label-syncer@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 93fcf241ce3405b530ce561082b29bd6d3afcea7 Mon Sep 17 00:00:00 2001 From: Pierre Prinetti <pierreprinetti@redhat.com> Date: Mon, 7 Nov 2022 11:17:43 +0100 Subject: [PATCH 160/360] Bump golang.org/x/crypto Update the depedency to the last commit that is compatible with Go v1.14. The [next commit][1] replaces calls to `io/ioutil` to its replacements in the `io` and `os` packages, which are not available in Go v1.14. [1]: https://go-review.googlesource.com/c/crypto/+/430797 --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index c51d7daaaf..0c7f0517e6 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,6 @@ module github.com/gophercloud/gophercloud go 1.14 require ( - golang.org/x/crypto v0.0.0-20211202192323-5770296d904e + golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 gopkg.in/yaml.v2 v2.4.0 ) diff --git a/go.sum b/go.sum index dec4af3cdc..0d5a1cde5a 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -golang.org/x/crypto v0.0.0-20211202192323-5770296d904e h1:MUP6MR3rJ7Gk9LEia0LP2ytiH6MuCfs7qYz+47jGdD8= -golang.org/x/crypto v0.0.0-20211202192323-5770296d904e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 h1:Y/gsMcFOcR+6S6f3YeMKl5g+dZMEWqcz5Czj/GWYbkM= +golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= From 90b1b76f03b2d14c7aa502b6356464d7fbc8da92 Mon Sep 17 00:00:00 2001 From: Pierre Prinetti <pierreprinetti@redhat.com> Date: Mon, 7 Nov 2022 15:55:53 +0100 Subject: [PATCH 161/360] tests: Fix Go v1.14 "go vet" We use "go get" to install test dependencies in Go v1.14. As a side effect, `go.sum` was polluted and `golang.org/x/crypto` was bumped to a version that is incompatible with Go v1.14. With this change, installing test dependencies no longer changes the state of the checked-out repository. --- .github/workflows/unit.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/unit.yml b/.github/workflows/unit.yml index bca05da058..dd6832b2af 100644 --- a/.github/workflows/unit.yml +++ b/.github/workflows/unit.yml @@ -29,6 +29,10 @@ jobs: - name: Setup environment run: | + # Changing into a different directory to avoid polluting go.sum with "go get" + cd "$(mktemp -d)" + + # we use "go get" for Go v1.14 go install github.com/wadey/gocovmerge@master || go get github.com/wadey/gocovmerge go install golang.org/x/tools/cmd/goimports@latest || go get golang.org/x/tools/cmd/goimports From 904e42c24a38470a489405762fa519bf564d0d3c Mon Sep 17 00:00:00 2001 From: Stas Kraev <staskraev@catalyst.net.nz> Date: Fri, 11 Nov 2022 15:18:53 +1300 Subject: [PATCH 162/360] Addressing review for l3-agent-scheduling --- .../networking/v2/extensions/layer3/l3_scheduling_test.go | 2 +- openstack/networking/v2/extensions/agents/doc.go | 4 ++-- openstack/networking/v2/extensions/agents/urls.go | 2 -- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/acceptance/openstack/networking/v2/extensions/layer3/l3_scheduling_test.go b/acceptance/openstack/networking/v2/extensions/layer3/l3_scheduling_test.go index 905246af18..f5a5ba27dc 100644 --- a/acceptance/openstack/networking/v2/extensions/layer3/l3_scheduling_test.go +++ b/acceptance/openstack/networking/v2/extensions/layer3/l3_scheduling_test.go @@ -47,7 +47,7 @@ func TestLayer3RouterScheduling(t *testing.T) { th.AssertNoErr(t, err) hostingAgents, err := routers.ExtractL3Agents(allPages) th.AssertNoErr(t, err) - th.AssertEquals(t, len(hostingAgents) > 0, true) + th.AssertIntGreaterOrEqual(t, len(hostingAgents), 0) hostingAgent := hostingAgents[0] t.Logf("Router %s is scheduled on %s", router.ID, hostingAgent.ID) diff --git a/openstack/networking/v2/extensions/agents/doc.go b/openstack/networking/v2/extensions/agents/doc.go index a52c709781..83bc09cfdb 100644 --- a/openstack/networking/v2/extensions/agents/doc.go +++ b/openstack/networking/v2/extensions/agents/doc.go @@ -142,7 +142,7 @@ Example to remove router from L3 agent agentID := "0e1095ae-6f36-40f3-8322-8e1c9a5e68ca" routerID := "e6fa0457-efc2-491d-ac12-17ab60417efd" - err = agents.RemoveL3Router(neutron, "0e1095ae-6f36-40f3-8322-8e1c9a5e68ca", "e6fa0457-efc2-491d-ac12-17ab60417efd").ExtractErr() + err = agents.RemoveL3Router(neutron, agentID, routerID).ExtractErr() if err != nil { log.Panic(err) } @@ -151,7 +151,7 @@ Example to schedule router to L3 agent agentID := "0e1095ae-6f36-40f3-8322-8e1c9a5e68ca" routerID := "e6fa0457-efc2-491d-ac12-17ab60417efd" - err = agents.ScheduleL3Router(neutron, agentID, agents.ScheduleL3RouterOpts{routerID}).ExtractErr() + err = agents.ScheduleL3Router(neutron, agentID, agents.ScheduleL3RouterOpts{RouterID: routerID}).ExtractErr() if err != nil { log.Panic(err) } diff --git a/openstack/networking/v2/extensions/agents/urls.go b/openstack/networking/v2/extensions/agents/urls.go index d4581ea3e7..3ee3e02dcd 100644 --- a/openstack/networking/v2/extensions/agents/urls.go +++ b/openstack/networking/v2/extensions/agents/urls.go @@ -46,8 +46,6 @@ func listDHCPNetworksURL(c *gophercloud.ServiceClient, id string) string { } func listL3RoutersURL(c *gophercloud.ServiceClient, id string) string { - // TODO - // hmm list should be the plain l3RoutersURL but dhcp example tell otherwise return l3RoutersURL(c, id) } From 918cd8378747db5360fbc5810eb6f3cf229a8457 Mon Sep 17 00:00:00 2001 From: Stanislav <mail@kraev.me> Date: Sat, 12 Nov 2022 09:14:55 +1300 Subject: [PATCH 163/360] Fixes misspel in l3-scheduling acceptance test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Martin André <martin.andre@gmail.com> --- .../networking/v2/extensions/layer3/l3_scheduling_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acceptance/openstack/networking/v2/extensions/layer3/l3_scheduling_test.go b/acceptance/openstack/networking/v2/extensions/layer3/l3_scheduling_test.go index f5a5ba27dc..ec32dfda11 100644 --- a/acceptance/openstack/networking/v2/extensions/layer3/l3_scheduling_test.go +++ b/acceptance/openstack/networking/v2/extensions/layer3/l3_scheduling_test.go @@ -47,7 +47,7 @@ func TestLayer3RouterScheduling(t *testing.T) { th.AssertNoErr(t, err) hostingAgents, err := routers.ExtractL3Agents(allPages) th.AssertNoErr(t, err) - th.AssertIntGreaterOrEqual(t, len(hostingAgents), 0) + th.AssertIntGreaterOrEqual(t, len(hostingAgents), 1) hostingAgent := hostingAgents[0] t.Logf("Router %s is scheduled on %s", router.ID, hostingAgent.ID) From 022c50f253ebd8ebcd5fedeec7141307411cd74c Mon Sep 17 00:00:00 2001 From: artem_lifshits <artem.lifshits@t-systems.com> Date: Wed, 16 Nov 2022 11:11:07 +0300 Subject: [PATCH 164/360] Add available domain listing --- .../openstack/identity/v3/domains_test.go | 17 +++++ openstack/identity/v3/domains/requests.go | 8 +++ .../identity/v3/domains/testing/fixtures.go | 71 +++++++++++++++++++ .../v3/domains/testing/requests_test.go | 20 ++++++ openstack/identity/v3/domains/urls.go | 4 ++ 5 files changed, 120 insertions(+) diff --git a/acceptance/openstack/identity/v3/domains_test.go b/acceptance/openstack/identity/v3/domains_test.go index 48ad8247c1..5e9d06b266 100644 --- a/acceptance/openstack/identity/v3/domains_test.go +++ b/acceptance/openstack/identity/v3/domains_test.go @@ -12,6 +12,23 @@ import ( th "github.com/gophercloud/gophercloud/testhelper" ) +func TestDomainsListAvailable(t *testing.T) { + clients.RequireAdmin(t) + + client, err := clients.NewIdentityV3Client() + th.AssertNoErr(t, err) + + allPages, err := domains.ListAvailable(client).AllPages() + th.AssertNoErr(t, err) + + allDomains, err := domains.ExtractDomains(allPages) + th.AssertNoErr(t, err) + + for _, domain := range allDomains { + tools.PrintResource(t, domain) + } +} + func TestDomainsList(t *testing.T) { clients.RequireAdmin(t) diff --git a/openstack/identity/v3/domains/requests.go b/openstack/identity/v3/domains/requests.go index 78847c8794..bf911d05c7 100644 --- a/openstack/identity/v3/domains/requests.go +++ b/openstack/identity/v3/domains/requests.go @@ -41,6 +41,14 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa }) } +// ListAvailable enumerates the domains which are available to a specific user. +func ListAvailable(client *gophercloud.ServiceClient) pagination.Pager { + url := listAvailableURL(client) + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return DomainPage{pagination.LinkedPageBase{PageResult: r}} + }) +} + // Get retrieves details on a single domain, by ID. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { resp, err := client.Get(getURL(client, id), &r.Body, nil) diff --git a/openstack/identity/v3/domains/testing/fixtures.go b/openstack/identity/v3/domains/testing/fixtures.go index 87ac561b5a..db328831b2 100644 --- a/openstack/identity/v3/domains/testing/fixtures.go +++ b/openstack/identity/v3/domains/testing/fixtures.go @@ -10,6 +10,37 @@ import ( "github.com/gophercloud/gophercloud/testhelper/client" ) +// ListAvailableOutput provides a single page of available domain results. +const ListAvailableOutput = ` +{ + "domains": [ + { + "id": "52af04aec5f84182b06959d2775d2000", + "name": "TestDomain", + "description": "Testing domain", + "enabled": false, + "links": { + "self": "https://example.com/v3/domains/52af04aec5f84182b06959d2775d2000" + } + }, + { + "id": "a720688fb87f4575a4c000d818061eae", + "name": "ProdDomain", + "description": "Production domain", + "enabled": true, + "links": { + "self": "https://example.com/v3/domains/a720688fb87f4575a4c000d818061eae" + } + } + ], + "links": { + "next": null, + "self": "https://example.com/v3/auth/domains", + "previous": null + } +} +` + // ListOutput provides a single page of Domain results. const ListOutput = ` { @@ -87,6 +118,28 @@ const UpdateOutput = ` } ` +// ProdDomain is a domain fixture. +var ProdDomain = domains.Domain{ + Enabled: true, + ID: "a720688fb87f4575a4c000d818061eae", + Links: map[string]interface{}{ + "self": "https://example.com/v3/domains/a720688fb87f4575a4c000d818061eae", + }, + Name: "ProdDomain", + Description: "Production domain", +} + +// TestDomain is a domain fixture. +var TestDomain = domains.Domain{ + Enabled: false, + ID: "52af04aec5f84182b06959d2775d2000", + Links: map[string]interface{}{ + "self": "https://example.com/v3/domains/52af04aec5f84182b06959d2775d2000", + }, + Name: "TestDomain", + Description: "Testing domain", +} + // FirstDomain is the first domain in the List request. var FirstDomain = domains.Domain{ Enabled: true, @@ -119,9 +172,27 @@ var SecondDomainUpdated = domains.Domain{ Description: "Staging Domain", } +// ExpectedAvailableDomainsSlice is the slice of domains expected to be returned +// from ListAvailableOutput. +var ExpectedAvailableDomainsSlice = []domains.Domain{TestDomain, ProdDomain} + // ExpectedDomainsSlice is the slice of domains expected to be returned from ListOutput. var ExpectedDomainsSlice = []domains.Domain{FirstDomain, SecondDomain} +// HandleListAvailableDomainsSuccessfully creates an HTTP handler at `/auth/domains` +// on the test handler mux that responds with a list of two domains. +func HandleListAvailableDomainsSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/auth/domains", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "Accept", "application/json") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, ListAvailableOutput) + }) +} + // HandleListDomainsSuccessfully creates an HTTP handler at `/domains` on the // test handler mux that responds with a list of two domains. func HandleListDomainsSuccessfully(t *testing.T) { diff --git a/openstack/identity/v3/domains/testing/requests_test.go b/openstack/identity/v3/domains/testing/requests_test.go index 07eeb06ca0..0f8d6fc7cf 100644 --- a/openstack/identity/v3/domains/testing/requests_test.go +++ b/openstack/identity/v3/domains/testing/requests_test.go @@ -9,6 +9,26 @@ import ( "github.com/gophercloud/gophercloud/testhelper/client" ) +func TestListAvailableDomains(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListAvailableDomainsSuccessfully(t) + + count := 0 + err := domains.ListAvailable(client.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) { + count++ + + actual, err := domains.ExtractDomains(page) + th.AssertNoErr(t, err) + + th.CheckDeepEquals(t, ExpectedAvailableDomainsSlice, actual) + + return true, nil + }) + th.AssertNoErr(t, err) + th.CheckEquals(t, count, 1) +} + func TestListDomains(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() diff --git a/openstack/identity/v3/domains/urls.go b/openstack/identity/v3/domains/urls.go index b0c21b80be..902532cc39 100644 --- a/openstack/identity/v3/domains/urls.go +++ b/openstack/identity/v3/domains/urls.go @@ -2,6 +2,10 @@ package domains import "github.com/gophercloud/gophercloud" +func listAvailableURL(client *gophercloud.ServiceClient) string { + return client.ServiceURL("auth", "domains") +} + func listURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("domains") } From 41a6bc5d3dd6bf0e19fd2937f80bfb5f7e6cf14a Mon Sep 17 00:00:00 2001 From: Robert Vasek <robert.vasek@cern.ch> Date: Thu, 20 Oct 2022 14:57:07 +0200 Subject: [PATCH 165/360] Manila: add Get for share-access-rules API --- .../sharedfilesystems/v2/shareaccessrules.go | 18 +++++ .../v2/shareaccessrules_test.go | 48 ++++++++++++++ .../v2/shareaccessrules/requests.go | 12 ++++ .../v2/shareaccessrules/results.go | 65 +++++++++++++++++++ .../v2/shareaccessrules/testing/fixtures.go | 45 +++++++++++++ .../shareaccessrules/testing/requests_test.go | 39 +++++++++++ .../v2/shareaccessrules/urls.go | 11 ++++ 7 files changed, 238 insertions(+) create mode 100644 acceptance/openstack/sharedfilesystems/v2/shareaccessrules.go create mode 100644 acceptance/openstack/sharedfilesystems/v2/shareaccessrules_test.go create mode 100644 openstack/sharedfilesystems/v2/shareaccessrules/requests.go create mode 100644 openstack/sharedfilesystems/v2/shareaccessrules/results.go create mode 100644 openstack/sharedfilesystems/v2/shareaccessrules/testing/fixtures.go create mode 100644 openstack/sharedfilesystems/v2/shareaccessrules/testing/requests_test.go create mode 100644 openstack/sharedfilesystems/v2/shareaccessrules/urls.go diff --git a/acceptance/openstack/sharedfilesystems/v2/shareaccessrules.go b/acceptance/openstack/sharedfilesystems/v2/shareaccessrules.go new file mode 100644 index 0000000000..8a8c746503 --- /dev/null +++ b/acceptance/openstack/sharedfilesystems/v2/shareaccessrules.go @@ -0,0 +1,18 @@ +package v2 + +import ( + "testing" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/shareaccessrules" +) + +func ShareAccessRuleGet(t *testing.T, client *gophercloud.ServiceClient, accessID string) (*shareaccessrules.ShareAccess, error) { + accessRule, err := shareaccessrules.Get(client, accessID).Extract() + if err != nil { + t.Logf("Failed to get share access rule %s: %v", accessID, err) + return nil, err + } + + return accessRule, nil +} diff --git a/acceptance/openstack/sharedfilesystems/v2/shareaccessrules_test.go b/acceptance/openstack/sharedfilesystems/v2/shareaccessrules_test.go new file mode 100644 index 0000000000..5da64a7252 --- /dev/null +++ b/acceptance/openstack/sharedfilesystems/v2/shareaccessrules_test.go @@ -0,0 +1,48 @@ +//go:build acceptance +// +build acceptance + +package v2 + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestShareAccessRulesGet(t *testing.T) { + client, err := clients.NewSharedFileSystemV2Client() + if err != nil { + t.Fatalf("Unable to create a shared file system client: %v", err) + } + + client.Microversion = "2.49" + + share, err := CreateShare(t, client) + if err != nil { + t.Fatalf("Unable to create a share: %v", err) + } + + defer DeleteShare(t, client, share) + + shareAccessRight, err := GrantAccess(t, client, share) + if err != nil { + t.Fatalf("Unable to grant access to share %s: %v", share.ID, err) + } + + accessRule, err := ShareAccessRuleGet(t, client, shareAccessRight.ID) + if err != nil { + t.Logf("Unable to get share access rule for share %s: %v", share.ID, err) + } + + tools.PrintResource(t, accessRule) + + th.AssertEquals(t, shareAccessRight.ID, accessRule.ID) + th.AssertEquals(t, shareAccessRight.ShareID, accessRule.ShareID) + th.AssertEquals(t, shareAccessRight.AccessType, accessRule.AccessType) + th.AssertEquals(t, shareAccessRight.AccessLevel, accessRule.AccessLevel) + th.AssertEquals(t, shareAccessRight.AccessTo, accessRule.AccessTo) + th.AssertEquals(t, shareAccessRight.AccessKey, accessRule.AccessKey) + th.AssertEquals(t, shareAccessRight.State, accessRule.State) +} diff --git a/openstack/sharedfilesystems/v2/shareaccessrules/requests.go b/openstack/sharedfilesystems/v2/shareaccessrules/requests.go new file mode 100644 index 0000000000..491085d5c1 --- /dev/null +++ b/openstack/sharedfilesystems/v2/shareaccessrules/requests.go @@ -0,0 +1,12 @@ +package shareaccessrules + +import ( + "github.com/gophercloud/gophercloud" +) + +// Get retrieves details about a share access rule. +func Get(client *gophercloud.ServiceClient, accessID string) (r GetResult) { + resp, err := client.Get(getURL(client, accessID), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} diff --git a/openstack/sharedfilesystems/v2/shareaccessrules/results.go b/openstack/sharedfilesystems/v2/shareaccessrules/results.go new file mode 100644 index 0000000000..2e54f5d410 --- /dev/null +++ b/openstack/sharedfilesystems/v2/shareaccessrules/results.go @@ -0,0 +1,65 @@ +package shareaccessrules + +import ( + "encoding/json" + "time" + + "github.com/gophercloud/gophercloud" +) + +// ShareAccess contains information associated with an OpenStack share access rule. +type ShareAccess struct { + // The UUID of the share to which you are granted or denied access. + ShareID string `json:"share_id"` + // The date and time stamp when the resource was created within the service’s database. + CreatedAt time.Time `json:"-"` + // The date and time stamp when the resource was last updated within the service’s database. + UpdatedAt time.Time `json:"-"` + // The access rule type. + AccessType string `json:"access_type"` + // The value that defines the access. The back end grants or denies the access to it. + AccessTo string `json:"access_to"` + // The access credential of the entity granted share access. + AccessKey string `json:"access_key"` + // The state of the access rule. + State string `json:"state"` + // The access level to the share. + AccessLevel string `json:"access_level"` + // The access rule ID. + ID string `json:"id"` + // Access rule metadata. + Metadata map[string]interface{} `json:"metadata"` +} + +func (r *ShareAccess) UnmarshalJSON(b []byte) error { + type tmp ShareAccess + 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 = ShareAccess(s.tmp) + + r.CreatedAt = time.Time(s.CreatedAt) + r.UpdatedAt = time.Time(s.UpdatedAt) + + return nil +} + +// GetResult contains the response body and error from a Get request. +type GetResult struct { + gophercloud.Result +} + +// Extract will get the ShareAccess object from the GetResult. +func (r GetResult) Extract() (*ShareAccess, error) { + var s struct { + ShareAccess *ShareAccess `json:"access"` + } + err := r.ExtractInto(&s) + return s.ShareAccess, err +} diff --git a/openstack/sharedfilesystems/v2/shareaccessrules/testing/fixtures.go b/openstack/sharedfilesystems/v2/shareaccessrules/testing/fixtures.go new file mode 100644 index 0000000000..2f053961f9 --- /dev/null +++ b/openstack/sharedfilesystems/v2/shareaccessrules/testing/fixtures.go @@ -0,0 +1,45 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/testhelper/client" +) + +const ( + shareAccessRulesEndpoint = "/share-access-rules" + shareAccessRuleID = "507bf114-36f2-4f56-8cf4-857985ca87c1" + shareID = "fb213952-2352-41b4-ad7b-2c4c69d13eef" +) + +var getResponse = `{ + "access": { + "access_level": "rw", + "state": "error", + "id": "507bf114-36f2-4f56-8cf4-857985ca87c1", + "share_id": "fb213952-2352-41b4-ad7b-2c4c69d13eef", + "access_type": "cert", + "access_to": "example.com", + "access_key": null, + "created_at": "2018-07-17T02:01:04.000000", + "updated_at": "2018-07-17T02:01:04.000000", + "metadata": { + "key1": "value1", + "key2": "value2" + } + } +}` + +func MockGetResponse(t *testing.T) { + th.Mux.HandleFunc(shareAccessRulesEndpoint+"/"+shareAccessRuleID, func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestHeader(t, r, "Accept", "application/json") + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, getResponse) + }) +} diff --git a/openstack/sharedfilesystems/v2/shareaccessrules/testing/requests_test.go b/openstack/sharedfilesystems/v2/shareaccessrules/testing/requests_test.go new file mode 100644 index 0000000000..a04b5f877b --- /dev/null +++ b/openstack/sharedfilesystems/v2/shareaccessrules/testing/requests_test.go @@ -0,0 +1,39 @@ +package testing + +import ( + "testing" + "time" + + "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/shareaccessrules" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestGet(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockGetResponse(t) + + resp := shareaccessrules.Get(client.ServiceClient(), "507bf114-36f2-4f56-8cf4-857985ca87c1") + th.AssertNoErr(t, resp.Err) + + accessRule, err := resp.Extract() + th.AssertNoErr(t, err) + + th.AssertDeepEquals(t, &shareaccessrules.ShareAccess{ + ShareID: "fb213952-2352-41b4-ad7b-2c4c69d13eef", + CreatedAt: time.Date(2018, 7, 17, 2, 1, 4, 0, time.UTC), + UpdatedAt: time.Date(2018, 7, 17, 2, 1, 4, 0, time.UTC), + AccessType: "cert", + AccessTo: "example.com", + AccessKey: "", + State: "error", + AccessLevel: "rw", + ID: "507bf114-36f2-4f56-8cf4-857985ca87c1", + Metadata: map[string]interface{}{ + "key1": "value1", + "key2": "value2", + }, + }, accessRule) +} diff --git a/openstack/sharedfilesystems/v2/shareaccessrules/urls.go b/openstack/sharedfilesystems/v2/shareaccessrules/urls.go new file mode 100644 index 0000000000..02766301e4 --- /dev/null +++ b/openstack/sharedfilesystems/v2/shareaccessrules/urls.go @@ -0,0 +1,11 @@ +package shareaccessrules + +import ( + "github.com/gophercloud/gophercloud" +) + +const shareAccessRulesEndpoint = "share-access-rules" + +func getURL(c *gophercloud.ServiceClient, accessID string) string { + return c.ServiceURL(shareAccessRulesEndpoint, accessID) +} From 64ed1bc1ee4b8e3a063c11bf7fdcd8c8328fe18a Mon Sep 17 00:00:00 2001 From: Pierre Prinetti <pierreprinetti@redhat.com> Date: Wed, 23 Nov 2022 12:50:29 +0100 Subject: [PATCH 166/360] objectstorage: Do not parse NoContent responses Some Swift instances respond with HTTP status code 204 when asked to list containers and there are no containers, or when asked to list objects on an empty container. Before this patch, Gophercloud would error because the header `content-type` is absent on the response. With this patch, objectstorage responses with HTTP status code 204 are immediately recognised as empty and their body is not read. --- .../objectstorage/v1/containers/results.go | 4 ++++ .../v1/containers/testing/fixtures.go | 13 +++++++++++ .../v1/containers/testing/requests_test.go | 12 ++++++++++ openstack/objectstorage/v1/objects/results.go | 4 ++++ .../v1/objects/testing/fixtures.go | 13 +++++++++++ .../v1/objects/testing/requests_test.go | 23 +++++++++++++++++++ pagination/http.go | 5 ++-- results.go | 5 ++++ 8 files changed, 77 insertions(+), 2 deletions(-) diff --git a/openstack/objectstorage/v1/containers/results.go b/openstack/objectstorage/v1/containers/results.go index e6e6a0487b..c6dc61fa9f 100644 --- a/openstack/objectstorage/v1/containers/results.go +++ b/openstack/objectstorage/v1/containers/results.go @@ -30,6 +30,10 @@ type ContainerPage struct { // IsEmpty returns true if a ListResult contains no container names. func (r ContainerPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + names, err := ExtractNames(r) return len(names) == 0, err } diff --git a/openstack/objectstorage/v1/containers/testing/fixtures.go b/openstack/objectstorage/v1/containers/testing/fixtures.go index 042e39d2fa..bc623b3149 100644 --- a/openstack/objectstorage/v1/containers/testing/fixtures.go +++ b/openstack/objectstorage/v1/containers/testing/fixtures.go @@ -94,6 +94,19 @@ func HandleListContainerNamesSuccessfully(t *testing.T) { }) } +// HandleListZeroContainerNames204 creates an HTTP handler at `/` on the test handler mux that +// responds with "204 No Content" when container names are requested. This happens on some, but not all, +// objectstorage instances. This case is peculiar in that the server sends no `content-type` header. +func HandleListZeroContainerNames204(t *testing.T) { + th.Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestHeader(t, r, "Accept", "text/plain") + + w.WriteHeader(http.StatusNoContent) + }) +} + // HandleCreateContainerSuccessfully creates an HTTP handler at `/testContainer` on the test handler mux that // responds with a `Create` response. func HandleCreateContainerSuccessfully(t *testing.T) { diff --git a/openstack/objectstorage/v1/containers/testing/requests_test.go b/openstack/objectstorage/v1/containers/testing/requests_test.go index dcfd1de753..e684f5d395 100644 --- a/openstack/objectstorage/v1/containers/testing/requests_test.go +++ b/openstack/objectstorage/v1/containers/testing/requests_test.go @@ -79,6 +79,18 @@ func TestListAllContainerNames(t *testing.T) { th.CheckDeepEquals(t, ExpectedListNames, actual) } +func TestListZeroContainerNames(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListZeroContainerNames204(t) + + allPages, err := containers.List(fake.ServiceClient(), &containers.ListOpts{Full: false}).AllPages() + th.AssertNoErr(t, err) + actual, err := containers.ExtractNames(allPages) + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, []string{}, actual) +} + func TestCreateContainer(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() diff --git a/openstack/objectstorage/v1/objects/results.go b/openstack/objectstorage/v1/objects/results.go index 75367d8349..0afc7e7bf9 100644 --- a/openstack/objectstorage/v1/objects/results.go +++ b/openstack/objectstorage/v1/objects/results.go @@ -70,6 +70,10 @@ type ObjectPage struct { // IsEmpty returns true if a ListResult contains no object names. func (r ObjectPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + names, err := ExtractNames(r) return len(names) == 0, err } diff --git a/openstack/objectstorage/v1/objects/testing/fixtures.go b/openstack/objectstorage/v1/objects/testing/fixtures.go index 21da11450c..0149d40e1e 100644 --- a/openstack/objectstorage/v1/objects/testing/fixtures.go +++ b/openstack/objectstorage/v1/objects/testing/fixtures.go @@ -162,6 +162,19 @@ func HandleListObjectNamesSuccessfully(t *testing.T) { }) } +// HandleListZeroObjectNames204 creates an HTTP handler at `/testContainer` on the test handler mux that +// responds with "204 No Content" when object names are requested. This happens on some, but not all, objectstorage +// instances. This case is peculiar in that the server sends no `content-type` header. +func HandleListZeroObjectNames204(t *testing.T) { + th.Mux.HandleFunc("/testContainer", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestHeader(t, r, "Accept", "text/plain") + + w.WriteHeader(http.StatusNoContent) + }) +} + // HandleCreateTextObjectSuccessfully creates an HTTP handler at `/testContainer/testObject` on the test handler mux // that responds with a `Create` response. A Content-Type of "text/plain" is expected. func HandleCreateTextObjectSuccessfully(t *testing.T, content string) { diff --git a/openstack/objectstorage/v1/objects/testing/requests_test.go b/openstack/objectstorage/v1/objects/testing/requests_test.go index 3b7156d0be..ff94ad0677 100644 --- a/openstack/objectstorage/v1/objects/testing/requests_test.go +++ b/openstack/objectstorage/v1/objects/testing/requests_test.go @@ -160,6 +160,29 @@ func TestListObjectNames(t *testing.T) { th.CheckEquals(t, count, 1) } +func TestListZeroObjectNames204(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListZeroObjectNames204(t) + + count := 0 + options := &objects.ListOpts{Full: false} + err := objects.List(fake.ServiceClient(), "testContainer", options).EachPage(func(page pagination.Page) (bool, error) { + count++ + actual, err := objects.ExtractNames(page) + if err != nil { + t.Errorf("Failed to extract container names: %v", err) + return false, err + } + + th.CheckDeepEquals(t, []string{}, actual) + + return true, nil + }) + th.AssertNoErr(t, err) + th.CheckEquals(t, 0, count) +} + func TestCreateObject(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() diff --git a/pagination/http.go b/pagination/http.go index df3503159a..7845cda13b 100644 --- a/pagination/http.go +++ b/pagination/http.go @@ -44,8 +44,9 @@ func PageResultFrom(resp *http.Response) (PageResult, error) { func PageResultFromParsed(resp *http.Response, body interface{}) PageResult { return PageResult{ Result: gophercloud.Result{ - Body: body, - Header: resp.Header, + Body: body, + StatusCode: resp.StatusCode, + Header: resp.Header, }, URL: *resp.Request.URL, } diff --git a/results.go b/results.go index 1b608103b7..b3ee9d5682 100644 --- a/results.go +++ b/results.go @@ -30,6 +30,11 @@ type Result struct { // this will be the deserialized JSON structure. Body interface{} + // 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 From 184da063ffe86fd509411dd4c737dd9a292bc199 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Kubica?= <pawel.kubica@comarch.pl> Date: Wed, 21 Sep 2022 10:18:24 +0200 Subject: [PATCH 167/360] Add support for volume type for db/v1/instance --- openstack/db/v1/instances/requests.go | 13 ++++++++++++- openstack/db/v1/instances/results.go | 2 ++ openstack/db/v1/instances/testing/fixtures.go | 16 ++++++++++------ .../db/v1/instances/testing/requests_test.go | 6 ++++-- 4 files changed, 28 insertions(+), 9 deletions(-) diff --git a/openstack/db/v1/instances/requests.go b/openstack/db/v1/instances/requests.go index f5243e70b1..73c7acc74e 100644 --- a/openstack/db/v1/instances/requests.go +++ b/openstack/db/v1/instances/requests.go @@ -53,6 +53,8 @@ type CreateOpts struct { // Specifies the volume size in gigabytes (GB). The value must be between 1 // and 300. Required. Size int + // Specifies the volume type. + VolumeType string // Name of the instance to create. The length of the name is limited to // 255 characters and any characters are permitted. Optional. Name string @@ -82,7 +84,6 @@ func (opts CreateOpts) ToInstanceCreateMap() (map[string]interface{}, error) { } instance := map[string]interface{}{ - "volume": map[string]int{"size": opts.Size}, "flavorRef": opts.FlavorRef, } @@ -123,6 +124,16 @@ func (opts CreateOpts) ToInstanceCreateMap() (map[string]interface{}, error) { instance["nics"] = networks } + volume := map[string]interface{}{ + "size": opts.Size, + } + + if opts.VolumeType != "" { + volume["type"] = opts.VolumeType + } + + instance["volume"] = volume + return map[string]interface{}{"instance": instance}, nil } diff --git a/openstack/db/v1/instances/results.go b/openstack/db/v1/instances/results.go index 3ac7b02480..5ec8db939d 100644 --- a/openstack/db/v1/instances/results.go +++ b/openstack/db/v1/instances/results.go @@ -14,6 +14,8 @@ import ( type Volume struct { // The size in GB of the volume Size int + // The type of the volume + Type string Used float64 } diff --git a/openstack/db/v1/instances/testing/fixtures.go b/openstack/db/v1/instances/testing/fixtures.go index c7e019e271..7a7bacc38b 100644 --- a/openstack/db/v1/instances/testing/fixtures.go +++ b/openstack/db/v1/instances/testing/fixtures.go @@ -48,7 +48,8 @@ var instance = ` "status": "BUILD", "updated": "` + timestamp + `", "volume": { - "size": 2 + "size": 2, + "type": "ssd" } } ` @@ -86,6 +87,7 @@ var instanceGet = ` "updated": "` + timestamp + `", "volume": { "size": 1, + "type": "ssd", "used": 0.12 }, "addresses": [ @@ -128,7 +130,8 @@ var createReq = ` } ], "volume": { - "size": 2 + "size": 2, + "type": "ssd" } } } @@ -166,7 +169,8 @@ var instanceWithFault = ` "status": "BUILD", "updated": "` + timestamp + `", "volume": { - "size": 2 + "size": 2, + "type": "ssd" }, "fault": { "message": "some error message", @@ -219,7 +223,7 @@ var expectedInstance = instances.Instance{ }, Name: "json_rack_instance", Status: "BUILD", - Volume: instances.Volume{Size: 2}, + Volume: instances.Volume{Size: 2, Type: "ssd"}, Datastore: datastores.DatastorePartial{ Type: "mysql", Version: "5.6", @@ -242,7 +246,7 @@ var expectedGetInstance = instances.Instance{ }, Name: "test", Status: "ACTIVE", - Volume: instances.Volume{Size: 1, Used: 0.12}, + Volume: instances.Volume{Size: 1, Type: "ssd", Used: 0.12}, Datastore: datastores.DatastorePartial{ Type: "mysql", Version: "5.6", @@ -270,7 +274,7 @@ var expectedInstanceWithFault = instances.Instance{ }, Name: "json_rack_instance", Status: "BUILD", - Volume: instances.Volume{Size: 2}, + Volume: instances.Volume{Size: 2, Type: "ssd"}, Datastore: datastores.DatastorePartial{ Type: "mysql", Version: "5.6", diff --git a/openstack/db/v1/instances/testing/requests_test.go b/openstack/db/v1/instances/testing/requests_test.go index 815793cbb6..a1575a4bb1 100644 --- a/openstack/db/v1/instances/testing/requests_test.go +++ b/openstack/db/v1/instances/testing/requests_test.go @@ -32,7 +32,8 @@ func TestCreate(t *testing.T) { }, }, }, - Size: 2, + Size: 2, + VolumeType: "ssd", } instance, err := instances.Create(fake.ServiceClient(), opts).Extract() @@ -62,7 +63,8 @@ func TestCreateWithFault(t *testing.T) { }, }, }, - Size: 2, + Size: 2, + VolumeType: "ssd", } instance, err := instances.Create(fake.ServiceClient(), opts).Extract() From 1a96f25b641fe73d0362d0e457123711b713696c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Kubica?= <pawel.kubica@comarch.pl> Date: Wed, 21 Sep 2022 22:35:34 +0200 Subject: [PATCH 168/360] Remove the non-existent Type field from the response --- openstack/db/v1/instances/results.go | 2 -- openstack/db/v1/instances/testing/fixtures.go | 7 +++---- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/openstack/db/v1/instances/results.go b/openstack/db/v1/instances/results.go index 5ec8db939d..3ac7b02480 100644 --- a/openstack/db/v1/instances/results.go +++ b/openstack/db/v1/instances/results.go @@ -14,8 +14,6 @@ import ( type Volume struct { // The size in GB of the volume Size int - // The type of the volume - Type string Used float64 } diff --git a/openstack/db/v1/instances/testing/fixtures.go b/openstack/db/v1/instances/testing/fixtures.go index 7a7bacc38b..233143e3d8 100644 --- a/openstack/db/v1/instances/testing/fixtures.go +++ b/openstack/db/v1/instances/testing/fixtures.go @@ -87,7 +87,6 @@ var instanceGet = ` "updated": "` + timestamp + `", "volume": { "size": 1, - "type": "ssd", "used": 0.12 }, "addresses": [ @@ -223,7 +222,7 @@ var expectedInstance = instances.Instance{ }, Name: "json_rack_instance", Status: "BUILD", - Volume: instances.Volume{Size: 2, Type: "ssd"}, + Volume: instances.Volume{Size: 2}, Datastore: datastores.DatastorePartial{ Type: "mysql", Version: "5.6", @@ -246,7 +245,7 @@ var expectedGetInstance = instances.Instance{ }, Name: "test", Status: "ACTIVE", - Volume: instances.Volume{Size: 1, Type: "ssd", Used: 0.12}, + Volume: instances.Volume{Size: 1, Used: 0.12}, Datastore: datastores.DatastorePartial{ Type: "mysql", Version: "5.6", @@ -274,7 +273,7 @@ var expectedInstanceWithFault = instances.Instance{ }, Name: "json_rack_instance", Status: "BUILD", - Volume: instances.Volume{Size: 2, Type: "ssd"}, + Volume: instances.Volume{Size: 2}, Datastore: datastores.DatastorePartial{ Type: "mysql", Version: "5.6", From 13195b74322da44b558c5af0fa769ae5b6f3750b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Kubica?= <pawel.kubica@comarch.pl> Date: Wed, 21 Sep 2022 23:01:08 +0200 Subject: [PATCH 169/360] Remove the non-existent Type field from the response - fixtures --- openstack/db/v1/instances/testing/fixtures.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/openstack/db/v1/instances/testing/fixtures.go b/openstack/db/v1/instances/testing/fixtures.go index 233143e3d8..782c048dc3 100644 --- a/openstack/db/v1/instances/testing/fixtures.go +++ b/openstack/db/v1/instances/testing/fixtures.go @@ -48,8 +48,7 @@ var instance = ` "status": "BUILD", "updated": "` + timestamp + `", "volume": { - "size": 2, - "type": "ssd" + "size": 2 } } ` @@ -168,8 +167,7 @@ var instanceWithFault = ` "status": "BUILD", "updated": "` + timestamp + `", "volume": { - "size": 2, - "type": "ssd" + "size": 2 }, "fault": { "message": "some error message", From b0961d48fa26a34aa512510824eacf4f7c24147e Mon Sep 17 00:00:00 2001 From: Pierre Prinetti <pierreprinetti@redhat.com> Date: Wed, 23 Nov 2022 16:36:38 +0100 Subject: [PATCH 170/360] objectstorage testing: Fix order of assertions Fix confusing error messages on failing tests. The assertions all required "expected" before "actual" in their arguments. --- .../v1/containers/testing/requests_test.go | 4 ++-- .../v1/objects/testing/requests_test.go | 18 +++++++++--------- .../v1/swauth/testing/requests_test.go | 2 +- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/openstack/objectstorage/v1/containers/testing/requests_test.go b/openstack/objectstorage/v1/containers/testing/requests_test.go index dcfd1de753..213ab6a273 100644 --- a/openstack/objectstorage/v1/containers/testing/requests_test.go +++ b/openstack/objectstorage/v1/containers/testing/requests_test.go @@ -30,7 +30,7 @@ func TestListContainerInfo(t *testing.T) { return true, nil }) th.AssertNoErr(t, err) - th.CheckEquals(t, count, 1) + th.CheckEquals(t, 1, count) } func TestListAllContainerInfo(t *testing.T) { @@ -64,7 +64,7 @@ func TestListContainerNames(t *testing.T) { return true, nil }) th.AssertNoErr(t, err) - th.CheckEquals(t, count, 1) + th.CheckEquals(t, 1, count) } func TestListAllContainerNames(t *testing.T) { diff --git a/openstack/objectstorage/v1/objects/testing/requests_test.go b/openstack/objectstorage/v1/objects/testing/requests_test.go index 3b7156d0be..3a58f7833f 100644 --- a/openstack/objectstorage/v1/objects/testing/requests_test.go +++ b/openstack/objectstorage/v1/objects/testing/requests_test.go @@ -75,7 +75,7 @@ func TestDownloadWithLastModified(t *testing.T) { response2 := objects.Download(fake.ServiceClient(), "testContainer", "testObject", options2) content, err2 := response2.ExtractContent() th.AssertNoErr(t, err2) - th.AssertEquals(t, len(content), 0) + th.AssertEquals(t, 0, len(content)) } func TestListObjectInfo(t *testing.T) { @@ -95,7 +95,7 @@ func TestListObjectInfo(t *testing.T) { return true, nil }) th.AssertNoErr(t, err) - th.CheckEquals(t, count, 1) + th.CheckEquals(t, 1, count) } func TestListObjectSubdir(t *testing.T) { @@ -115,7 +115,7 @@ func TestListObjectSubdir(t *testing.T) { return true, nil }) th.AssertNoErr(t, err) - th.CheckEquals(t, count, 1) + th.CheckEquals(t, 1, count) } func TestListObjectNames(t *testing.T) { @@ -139,7 +139,7 @@ func TestListObjectNames(t *testing.T) { return true, nil }) th.AssertNoErr(t, err) - th.CheckEquals(t, count, 1) + th.CheckEquals(t, 1, count) // Check with delimiter. count = 0 @@ -157,7 +157,7 @@ func TestListObjectNames(t *testing.T) { return true, nil }) th.AssertNoErr(t, err) - th.CheckEquals(t, count, 1) + th.CheckEquals(t, 1, count) } func TestCreateObject(t *testing.T) { @@ -290,7 +290,7 @@ func TestGetObject(t *testing.T) { } actualHeaders, err := objects.Get(fake.ServiceClient(), "testContainer", "testObject", getOpts).Extract() th.AssertNoErr(t, err) - th.AssertEquals(t, actualHeaders.StaticLargeObject, true) + th.AssertEquals(t, true, actualHeaders.StaticLargeObject) } func TestETag(t *testing.T) { @@ -303,7 +303,7 @@ func TestETag(t *testing.T) { _, headers, _, err := createOpts.ToObjectCreateParams() th.AssertNoErr(t, err) _, ok := headers["ETag"] - th.AssertEquals(t, ok, false) + th.AssertEquals(t, false, ok) hash := md5.New() io.WriteString(hash, content) @@ -316,7 +316,7 @@ func TestETag(t *testing.T) { _, headers, _, err = createOpts.ToObjectCreateParams() th.AssertNoErr(t, err) - th.AssertEquals(t, headers["ETag"], localChecksum) + th.AssertEquals(t, localChecksum, headers["ETag"]) } func TestObjectCreateParamsWithoutSeek(t *testing.T) { @@ -329,7 +329,7 @@ func TestObjectCreateParamsWithoutSeek(t *testing.T) { th.AssertNoErr(t, err) _, ok := reader.(io.ReadSeeker) - th.AssertEquals(t, ok, true) + th.AssertEquals(t, true, ok) c, err := ioutil.ReadAll(reader) th.AssertNoErr(t, err) diff --git a/openstack/objectstorage/v1/swauth/testing/requests_test.go b/openstack/objectstorage/v1/swauth/testing/requests_test.go index 46571f6117..0850aeff45 100644 --- a/openstack/objectstorage/v1/swauth/testing/requests_test.go +++ b/openstack/objectstorage/v1/swauth/testing/requests_test.go @@ -23,7 +23,7 @@ func TestAuth(t *testing.T) { swiftClient, err := swauth.NewObjectStorageV1(providerClient, authOpts) th.AssertNoErr(t, err) - th.AssertEquals(t, swiftClient.TokenID, AuthResult.Token) + th.AssertEquals(t, AuthResult.Token, swiftClient.TokenID) } func TestBadAuth(t *testing.T) { From 6266ecf266002a63a07b2469041a841acfeac8eb Mon Sep 17 00:00:00 2001 From: Pierre Prinetti <pierreprinetti@redhat.com> Date: Mon, 12 Sep 2022 10:50:17 +0200 Subject: [PATCH 171/360] Update changelog preparing v1.1.0 The changelog for v1.1.0 is the curated output of: ``` gh pr list \ --state merged \ --search 'milestone:v1.1.0' \ --json number,title \ --template \ '{{range .}}* {{ printf "[GH-%v](https://github.com/gophercloud/gophercloud/pull/%v)" .number .number }} {{ .title }} {{end}}' ``` --- CHANGELOG.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c8137c10d..2cef7d88e2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,24 @@ +## 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 From eb9a8487891b4fe78471b51251dc3e4796fd18e7 Mon Sep 17 00:00:00 2001 From: Pierre Prinetti <pierreprinetti@redhat.com> Date: Wed, 7 Dec 2022 14:26:50 +0100 Subject: [PATCH 172/360] Update changelog preparing v1.1.1 --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2cef7d88e2..d5ebe26269 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## 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 From ad6b608d7acb13cec231ed213cf9423e5e9c1b2a Mon Sep 17 00:00:00 2001 From: emilmaruszczak <emil.maruszczak@thehutgroup.com> Date: Wed, 14 Dec 2022 15:06:53 +0100 Subject: [PATCH 173/360] Use precreated glance registered limit --- .../openstack/identity/v3/limits_test.go | 26 ++++++++++++------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/acceptance/openstack/identity/v3/limits_test.go b/acceptance/openstack/identity/v3/limits_test.go index a4d6996cdf..b696055770 100644 --- a/acceptance/openstack/identity/v3/limits_test.go +++ b/acceptance/openstack/identity/v3/limits_test.go @@ -43,7 +43,7 @@ func TestLimitsList(t *testing.T) { func TestCreateLimits(t *testing.T) { limitDescription := tools.RandomString("TESTLIMITS-DESC-", 8) resourceLimit := tools.RandomInt(1, 100) - resourceName := "volume" + resourceName := "image_size_total" clients.RequireAdmin(t) @@ -53,21 +53,29 @@ func TestCreateLimits(t *testing.T) { project, err := CreateProject(t, client, nil) th.AssertNoErr(t, err) - createServiceOpts := &services.CreateOpts{ - Type: resourceName, - Extra: map[string]interface{}{}, - } - - service, err := CreateService(t, client, createServiceOpts) + // Find image service (glance on Devstack) which has precreated registered limits. + // TODO: Use registered limits API to create global limit and then overwrite it with limit. + allPages, err := services.List(client, nil).AllPages() th.AssertNoErr(t, err) + svList, err := services.ExtractServices(allPages) + serviceID := "" + for _, service := range svList { + if service.Type == "image" { + serviceID = service.ID + break + } + } + th.AssertIntGreaterOrEqual(t, len(serviceID), 1) + createOpts := limits.BatchCreateOpts{ limits.CreateOpts{ - ServiceID: service.ID, + ServiceID: serviceID, ProjectID: project.ID, ResourceName: resourceName, ResourceLimit: resourceLimit, Description: limitDescription, + RegionID: "RegionOne", }, } @@ -77,6 +85,6 @@ func TestCreateLimits(t *testing.T) { th.AssertEquals(t, limitDescription, createdLimits[0].Description) th.AssertEquals(t, resourceLimit, createdLimits[0].ResourceLimit) th.AssertEquals(t, resourceName, createdLimits[0].ResourceName) - th.AssertEquals(t, service.ID, createdLimits[0].ServiceID) + th.AssertEquals(t, serviceID, createdLimits[0].ServiceID) th.AssertEquals(t, project.ID, createdLimits[0].ProjectID) } From 422f5a5054a97c83da8eac158bf48a0edc7d29eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= <m.andre@redhat.com> Date: Wed, 14 Dec 2022 13:05:24 +0100 Subject: [PATCH 174/360] [CI] Force zaqarclient branch in clustering and messaging jobs It would otherwise pick master and fail to install in the train and ussuri jobs due to older versions of Ubuntu not having a recent enough python. --- .github/workflows/functional-clustering.yaml | 1 + .github/workflows/functional-messaging.yaml | 1 + 2 files changed, 2 insertions(+) diff --git a/.github/workflows/functional-clustering.yaml b/.github/workflows/functional-clustering.yaml index 9bd52ee967..7c1c6bc6a7 100644 --- a/.github/workflows/functional-clustering.yaml +++ b/.github/workflows/functional-clustering.yaml @@ -44,6 +44,7 @@ jobs: conf_overrides: | enable_plugin senlin https://opendev.org/openstack/senlin ${{ matrix.openstack_version }} enable_plugin zaqar https://opendev.org/openstack/zaqar ${{ matrix.openstack_version }} + ZAQARCLIENT_BRANCH=${{ matrix.openstack_version }} - name: Checkout go uses: actions/setup-go@v3 with: diff --git a/.github/workflows/functional-messaging.yaml b/.github/workflows/functional-messaging.yaml index 8215fa4c38..92dee6f912 100644 --- a/.github/workflows/functional-messaging.yaml +++ b/.github/workflows/functional-messaging.yaml @@ -43,6 +43,7 @@ jobs: branch: ${{ matrix.openstack_version }} conf_overrides: | enable_plugin zaqar https://opendev.org/openstack/zaqar ${{ matrix.openstack_version }} + ZAQARCLIENT_BRANCH=${{ matrix.openstack_version }} - name: Checkout go uses: actions/setup-go@v3 with: From 0899f6d59e43c113e2c69e7e61ee604b46b88685 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= <m.andre@redhat.com> Date: Wed, 14 Dec 2022 13:42:45 +0100 Subject: [PATCH 175/360] [CI] Use eol tags for ussuri and train containerinfra jobs The stable/ussuri and stable/train branches no longer exist in magnum repo. Use tags pointing to latest commits of relevant branches instead. Also force magnumclient to be in sync with the tested branch. --- .../workflows/functional-containerinfra.yaml | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/.github/workflows/functional-containerinfra.yaml b/.github/workflows/functional-containerinfra.yaml index 8e320a1ff5..98733788f0 100644 --- a/.github/workflows/functional-containerinfra.yaml +++ b/.github/workflows/functional-containerinfra.yaml @@ -10,28 +10,42 @@ jobs: strategy: fail-fast: false matrix: - name: ["master"] - openstack_version: ["master"] - ubuntu_version: ["20.04"] include: + - name: "master" + openstack_version: "master" + ubuntu_version: "20.04" + devstack_conf_overrides: | + enable_plugin magnum https://opendev.org/openstack/magnum master - name: "yoga" openstack_version: "stable/yoga" ubuntu_version: "20.04" + devstack_conf_overrides: | + enable_plugin magnum https://opendev.org/openstack/magnum stable/yoga - name: "xena" openstack_version: "stable/xena" ubuntu_version: "20.04" + devstack_conf_overrides: | + enable_plugin magnum https://opendev.org/openstack/magnum stable/xena - name: "wallaby" openstack_version: "stable/wallaby" ubuntu_version: "20.04" + devstack_conf_overrides: | + enable_plugin magnum https://opendev.org/openstack/magnum stable/wallaby - name: "victoria" openstack_version: "stable/victoria" ubuntu_version: "20.04" + devstack_conf_overrides: | + enable_plugin magnum https://opendev.org/openstack/magnum stable/victoria - name: "ussuri" openstack_version: "stable/ussuri" ubuntu_version: "18.04" + devstack_conf_overrides: | + enable_plugin magnum https://opendev.org/openstack/magnum ussuri-eol - name: "train" openstack_version: "stable/train" ubuntu_version: "18.04" + devstack_conf_overrides: | + enable_plugin magnum https://opendev.org/openstack/magnum train-eol runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Deploy OpenStack ${{ matrix.name }} with Magnum and run containerinfra acceptance tests steps: @@ -44,10 +58,11 @@ jobs: conf_overrides: | enable_plugin barbican https://opendev.org/openstack/barbican ${{ matrix.openstack_version }} enable_plugin heat https://opendev.org/openstack/heat ${{ matrix.openstack_version }} - enable_plugin magnum https://opendev.org/openstack/magnum ${{ matrix.openstack_version }} GLANCE_LIMIT_IMAGE_SIZE_TOTAL=5000 SWIFT_MAX_FILE_SIZE=5368709122 KEYSTONE_ADMIN_ENDPOINT=true + MAGNUMCLIENT_BRANCH=${{ matrix.openstack_version }} + ${{ matrix.devstack_conf_overrides }} enabled_services: 'h-eng,h-api,h-api-cfn,h-api-cw' - name: Checkout go uses: actions/setup-go@v3 From 9809b6a9e2bfdf5a3c7f4cdf94750f535279e590 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= <m.andre@redhat.com> Date: Wed, 14 Dec 2022 13:44:30 +0100 Subject: [PATCH 176/360] [CI] Use eol tags for train dns job The stable/train branch no longer exist in designate repo. Use tag pointing to latest commits of the branch instead. --- .github/workflows/functional-dns.yaml | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/.github/workflows/functional-dns.yaml b/.github/workflows/functional-dns.yaml index 782c9e05d4..5b53df3690 100644 --- a/.github/workflows/functional-dns.yaml +++ b/.github/workflows/functional-dns.yaml @@ -11,28 +11,42 @@ jobs: strategy: fail-fast: false matrix: - name: ["master"] - openstack_version: ["master"] - ubuntu_version: ["20.04"] include: + - name: "master" + openstack_version: "master" + ubuntu_version: "20.04" + devstack_conf_overrides: | + enable_plugin designate https://opendev.org/openstack/designate master - name: "yoga" openstack_version: "stable/yoga" ubuntu_version: "20.04" + devstack_conf_overrides: | + enable_plugin designate https://opendev.org/openstack/designate stable/yoga - name: "xena" openstack_version: "stable/xena" ubuntu_version: "20.04" + devstack_conf_overrides: | + enable_plugin designate https://opendev.org/openstack/designate stable/xena - name: "wallaby" openstack_version: "stable/wallaby" ubuntu_version: "20.04" + devstack_conf_overrides: | + enable_plugin designate https://opendev.org/openstack/designate stable/wallaby - name: "victoria" openstack_version: "stable/victoria" ubuntu_version: "20.04" + devstack_conf_overrides: | + enable_plugin designate https://opendev.org/openstack/designate stable/victoria - name: "ussuri" openstack_version: "stable/ussuri" ubuntu_version: "18.04" + devstack_conf_overrides: | + enable_plugin designate https://opendev.org/openstack/designate stable/ussuri - name: "train" openstack_version: "stable/train" ubuntu_version: "18.04" + devstack_conf_overrides: | + enable_plugin designate https://opendev.org/openstack/designate train-eol runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Deploy OpenStack ${{ matrix.name }} with Designate and run dns acceptance tests steps: @@ -43,7 +57,7 @@ jobs: with: branch: ${{ matrix.openstack_version }} conf_overrides: | - enable_plugin designate https://opendev.org/openstack/designate ${{ matrix.openstack_version }} + ${{ matrix.devstack_conf_overrides }} enabled_services: 'designate,designate-central,designate-api,designate-worker,designate-producer,designate-mdns' - name: Checkout go uses: actions/setup-go@v3 From 421c693f3eb08908ad8ba85ececdcd9bf789397e Mon Sep 17 00:00:00 2001 From: Alexander Birkner <alexander.birkner@g-portal.cloud> Date: Thu, 22 Dec 2022 01:54:24 +0100 Subject: [PATCH 177/360] Added target "rebuild" --- openstack/baremetal/v1/nodes/requests.go | 1 + 1 file changed, 1 insertion(+) diff --git a/openstack/baremetal/v1/nodes/requests.go b/openstack/baremetal/v1/nodes/requests.go index cd41662427..2515bf1587 100644 --- a/openstack/baremetal/v1/nodes/requests.go +++ b/openstack/baremetal/v1/nodes/requests.go @@ -61,6 +61,7 @@ const ( TargetAdopt TargetProvisionState = "adopt" TargetRescue TargetProvisionState = "rescue" TargetUnrescue TargetProvisionState = "unrescue" + TargetRebuild TargetProvisionState = "rebuild" ) // ListOpts allows the filtering and sorting of paginated collections through From 9fc81fda7d043b8cacf98bfa79656c8e4d949cc8 Mon Sep 17 00:00:00 2001 From: emilmaruszczak <emil.maruszczak@thehutgroup.com> Date: Thu, 22 Dec 2022 13:08:40 +0100 Subject: [PATCH 178/360] Ensure system_scope token for limit creation --- acceptance/openstack/identity/v3/limits_test.go | 6 ++++++ openstack/auth_env.go | 9 +++++++++ 2 files changed, 15 insertions(+) diff --git a/acceptance/openstack/identity/v3/limits_test.go b/acceptance/openstack/identity/v3/limits_test.go index b696055770..672787c273 100644 --- a/acceptance/openstack/identity/v3/limits_test.go +++ b/acceptance/openstack/identity/v3/limits_test.go @@ -4,6 +4,7 @@ package v3 import ( + "os" "testing" "github.com/gophercloud/gophercloud/acceptance/clients" @@ -41,6 +42,10 @@ func TestLimitsList(t *testing.T) { } func TestCreateLimits(t *testing.T) { + err := os.Setenv("OS_SYSTEM_SCOPE", "all") + th.AssertNoErr(t, err) + defer os.Unsetenv("OS_SYSTEM_SCOPE") + limitDescription := tools.RandomString("TESTLIMITS-DESC-", 8) resourceLimit := tools.RandomInt(1, 100) resourceName := "image_size_total" @@ -87,4 +92,5 @@ func TestCreateLimits(t *testing.T) { th.AssertEquals(t, resourceName, createdLimits[0].ResourceName) th.AssertEquals(t, serviceID, createdLimits[0].ServiceID) th.AssertEquals(t, project.ID, createdLimits[0].ProjectID) + } diff --git a/openstack/auth_env.go b/openstack/auth_env.go index c801de5553..7c6d06f0c3 100644 --- a/openstack/auth_env.go +++ b/openstack/auth_env.go @@ -46,6 +46,7 @@ func AuthOptionsFromEnv() (gophercloud.AuthOptions, error) { 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 != "" { @@ -109,6 +110,13 @@ func AuthOptionsFromEnv() (gophercloud.AuthOptions, error) { } } + var scope *gophercloud.AuthScope + if systemScope == "all" { + scope = &gophercloud.AuthScope{ + System: true, + } + } + ao := gophercloud.AuthOptions{ IdentityEndpoint: authURL, UserID: userID, @@ -122,6 +130,7 @@ func AuthOptionsFromEnv() (gophercloud.AuthOptions, error) { ApplicationCredentialID: applicationCredentialID, ApplicationCredentialName: applicationCredentialName, ApplicationCredentialSecret: applicationCredentialSecret, + Scope: scope, } return ao, nil From 66324e6e497389328c2ed734749e59f64209b41a Mon Sep 17 00:00:00 2001 From: Emil Maruszczak <emil.maruszczak@thehutgroup.com> Date: Thu, 22 Dec 2022 14:19:01 +0100 Subject: [PATCH 179/360] Add mappings create logic and test --- .../identity/v3/extensions/federation/doc.go | 33 +++++++ .../v3/extensions/federation/requests.go | 31 +++++++ .../v3/extensions/federation/results.go | 20 +++++ .../extensions/federation/testing/fixtures.go | 87 +++++++++++++++++++ .../federation/testing/requests_test.go | 41 +++++++++ .../identity/v3/extensions/federation/urls.go | 4 + 6 files changed, 216 insertions(+) diff --git a/openstack/identity/v3/extensions/federation/doc.go b/openstack/identity/v3/extensions/federation/doc.go index e8b9369aaf..290b24eafe 100644 --- a/openstack/identity/v3/extensions/federation/doc.go +++ b/openstack/identity/v3/extensions/federation/doc.go @@ -12,5 +12,38 @@ Example to List Mappings if err != nil { panic(err) } + +Example to Create Mappings + + createOpts := federation.CreateMappingOpts{ + Rules: []federation.MappingRule{ + { + Local: []federation.RuleLocal{ + { + User: &federation.RuleUser{ + Name: "{0}", + }, + }, + { + Group: &federation.Group{ + ID: "0cd5e9", + }, + }, + }, + Remote: []federation.RuleRemote{ + { + Type: "UserName", + }, + { + Type: "orgPersonType", + NotAnyOf: []string{ + "Contractor", + "Guest", + }, + }, + }, + }, + }, + } */ package federation diff --git a/openstack/identity/v3/extensions/federation/requests.go b/openstack/identity/v3/extensions/federation/requests.go index d42caf2e0c..f180c2282f 100644 --- a/openstack/identity/v3/extensions/federation/requests.go +++ b/openstack/identity/v3/extensions/federation/requests.go @@ -11,3 +11,34 @@ func ListMappings(client *gophercloud.ServiceClient) pagination.Pager { return MappingsPage{pagination.LinkedPageBase{PageResult: r}} }) } + +// CreateMappingOptsBuilder allows extensions to add additional parameters to +// the Create request. +type CreateMappingOptsBuilder interface { + ToMappingCreateMap() (map[string]interface{}, error) +} + +// UpdateMappingOpts provides options for creating a mapping. +type CreateMappingOpts struct { + // The list of rules used to map remote users into local users + Rules []MappingRule `json:"rules"` +} + +// ToMappingCreateMap formats a CreateMappingOpts into a create request. +func (opts CreateMappingOpts) ToMappingCreateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "mapping") +} + +// CreateMapping creates a new Mapping. +func CreateMapping(client *gophercloud.ServiceClient, mappingID string, opts CreateMappingOptsBuilder) (r CreateMappingResult) { + b, err := opts.ToMappingCreateMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Put(mappingsResourceURL(client, mappingID), &b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{201}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} diff --git a/openstack/identity/v3/extensions/federation/results.go b/openstack/identity/v3/extensions/federation/results.go index 745546fd7b..e23a7e1499 100644 --- a/openstack/identity/v3/extensions/federation/results.go +++ b/openstack/identity/v3/extensions/federation/results.go @@ -1,6 +1,7 @@ package federation import ( + "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) @@ -130,6 +131,25 @@ type RuleUser struct { Type *UserType `json:"type,omitempty"` } +type mappingResult struct { + gophercloud.Result +} + +// Extract interprets any mappingResult as a Mapping. +func (c mappingResult) Extract() (*Mapping, error) { + var s struct { + Mapping *Mapping `json:"mapping"` + } + err := c.ExtractInto(&s) + return s.Mapping, err +} + +// CreateMappingResult is the response from a CreateMapping operation. +// Call its Extract method to interpret it as a Mapping. +type CreateMappingResult struct { + mappingResult +} + // MappingsPage is a single page of Mapping results. type MappingsPage struct { pagination.LinkedPageBase diff --git a/openstack/identity/v3/extensions/federation/testing/fixtures.go b/openstack/identity/v3/extensions/federation/testing/fixtures.go index 59785dfed3..2af384fcb2 100644 --- a/openstack/identity/v3/extensions/federation/testing/fixtures.go +++ b/openstack/identity/v3/extensions/federation/testing/fixtures.go @@ -56,6 +56,80 @@ const ListOutput = ` } ` +const CreateRequest = ` + { + "mapping": { + "rules": [ + { + "local": [ + { + "user": { + "name": "{0}" + } + }, + { + "group": { + "id": "0cd5e9" + } + } + ], + "remote": [ + { + "type": "UserName" + }, + { + "type": "orgPersonType", + "not_any_of": [ + "Contractor", + "Guest" + ] + } + ] + } + ] + } +} +` + +const CreateOutput = ` +{ + "mapping": { + "id": "ACME", + "links": { + "self": "http://example.com/identity/v3/OS-FEDERATION/mappings/ACME" + }, + "rules": [ + { + "local": [ + { + "user": { + "name": "{0}" + } + }, + { + "group": { + "id": "0cd5e9" + } + } + ], + "remote": [ + { + "type": "UserName" + }, + { + "type": "orgPersonType", + "not_any_of": [ + "Contractor", + "Guest" + ] + } + ] + } + ] + } +} +` + var MappingACME = federation.Mapping{ ID: "ACME", Links: map[string]interface{}{ @@ -107,3 +181,16 @@ func HandleListMappingsSuccessfully(t *testing.T) { fmt.Fprintf(w, ListOutput) }) } + +// HandleCreateMappingSuccessfully creates an HTTP handler at `/mappings` on the +// test handler mux that tests mapping creation. +func HandleCreateMappingSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/OS-FEDERATION/mappings/ACME", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PUT") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + th.TestJSONRequest(t, r, CreateRequest) + + w.WriteHeader(http.StatusCreated) + fmt.Fprintf(w, CreateOutput) + }) +} diff --git a/openstack/identity/v3/extensions/federation/testing/requests_test.go b/openstack/identity/v3/extensions/federation/testing/requests_test.go index 0bf53a4529..d0f54ca969 100644 --- a/openstack/identity/v3/extensions/federation/testing/requests_test.go +++ b/openstack/identity/v3/extensions/federation/testing/requests_test.go @@ -40,3 +40,44 @@ func TestListMappingsAllPages(t *testing.T) { th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedMappingsSlice, actual) } + +func TestCreateMappings(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleCreateMappingSuccessfully(t) + + createOpts := federation.CreateMappingOpts{ + Rules: []federation.MappingRule{ + { + Local: []federation.RuleLocal{ + { + User: &federation.RuleUser{ + Name: "{0}", + }, + }, + { + Group: &federation.Group{ + ID: "0cd5e9", + }, + }, + }, + Remote: []federation.RuleRemote{ + { + Type: "UserName", + }, + { + Type: "orgPersonType", + NotAnyOf: []string{ + "Contractor", + "Guest", + }, + }, + }, + }, + }, + } + + actual, err := federation.CreateMapping(client.ServiceClient(), "ACME", createOpts).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, MappingACME, *actual) +} diff --git a/openstack/identity/v3/extensions/federation/urls.go b/openstack/identity/v3/extensions/federation/urls.go index 8841262dca..07a08ae5eb 100644 --- a/openstack/identity/v3/extensions/federation/urls.go +++ b/openstack/identity/v3/extensions/federation/urls.go @@ -10,3 +10,7 @@ const ( func mappingsRootURL(c *gophercloud.ServiceClient) string { return c.ServiceURL(rootPath, mappingsPath) } + +func mappingsResourceURL(c *gophercloud.ServiceClient, mappingID string) string { + return c.ServiceURL(rootPath, mappingsPath, mappingID) +} From fc4a8356542ada176b771ed261162cb7d68c271f Mon Sep 17 00:00:00 2001 From: Emil Maruszczak <emil.maruszczak@thehutgroup.com> Date: Thu, 22 Dec 2022 15:35:46 +0100 Subject: [PATCH 180/360] Add acceptance test for mappings create --- .../openstack/identity/v3/federation_test.go | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/acceptance/openstack/identity/v3/federation_test.go b/acceptance/openstack/identity/v3/federation_test.go index 8afc7f9ad2..00cfcd7b2c 100644 --- a/acceptance/openstack/identity/v3/federation_test.go +++ b/acceptance/openstack/identity/v3/federation_test.go @@ -24,3 +24,45 @@ func TestListMappings(t *testing.T) { tools.PrintResource(t, mappings) } + +func TestCreateMapping(t *testing.T) { + clients.RequireAdmin(t) + + client, err := clients.NewIdentityV3Client() + th.AssertNoErr(t, err) + + createOpts := federation.CreateMappingOpts{ + Rules: []federation.MappingRule{ + { + Local: []federation.RuleLocal{ + { + User: &federation.RuleUser{ + Name: "{0}", + }, + }, + { + Group: &federation.Group{ + ID: "0cd5e9", + }, + }, + }, + Remote: []federation.RuleRemote{ + { + Type: "UserName", + }, + { + Type: "orgPersonType", + NotAnyOf: []string{ + "Contractor", + "Guest", + }, + }, + }, + }, + }, + } + + actual, err := federation.CreateMapping(client, "ACME", createOpts).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, createOpts.Rules[0], actual.Rules[0]) +} From 338a10ae6dac6b34bcff8967fc6bf4b65c628a16 Mon Sep 17 00:00:00 2001 From: Emil Maruszczak <emil.maruszczak@thehutgroup.com> Date: Tue, 27 Dec 2022 16:11:47 +0100 Subject: [PATCH 181/360] Use random mapping name --- acceptance/openstack/identity/v3/federation_test.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/acceptance/openstack/identity/v3/federation_test.go b/acceptance/openstack/identity/v3/federation_test.go index 00cfcd7b2c..1eae1386e1 100644 --- a/acceptance/openstack/identity/v3/federation_test.go +++ b/acceptance/openstack/identity/v3/federation_test.go @@ -25,7 +25,9 @@ func TestListMappings(t *testing.T) { tools.PrintResource(t, mappings) } -func TestCreateMapping(t *testing.T) { +func TestMappingsCRUD(t *testing.T) { + mappingName := tools.RandomString("TESTMAPPING-", 8) + clients.RequireAdmin(t) client, err := clients.NewIdentityV3Client() @@ -62,7 +64,7 @@ func TestCreateMapping(t *testing.T) { }, } - actual, err := federation.CreateMapping(client, "ACME", createOpts).Extract() + actual, err := federation.CreateMapping(client, mappingName, createOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, createOpts.Rules[0], actual.Rules[0]) } From 696832f62dd82ae90ae4ae5f160ec98e66be5568 Mon Sep 17 00:00:00 2001 From: Emil Maruszczak <emil.maruszczak@thehutgroup.com> Date: Tue, 27 Dec 2022 16:26:59 +0100 Subject: [PATCH 182/360] Add missing doc --- openstack/identity/v3/extensions/federation/doc.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/openstack/identity/v3/extensions/federation/doc.go b/openstack/identity/v3/extensions/federation/doc.go index 290b24eafe..c87eeb6609 100644 --- a/openstack/identity/v3/extensions/federation/doc.go +++ b/openstack/identity/v3/extensions/federation/doc.go @@ -45,5 +45,10 @@ Example to Create Mappings }, }, } + + createdMapping, err := federation.CreateMapping(identityClient, "ACME", createOpts).Extract() + if err != nil { + panic(err) + } */ package federation From f92a181f50471d17b1343809d1b5b90bc8674f35 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Jan 2023 09:12:26 +0000 Subject: [PATCH 183/360] Bump mheap/github-action-required-labels from 2 to 3 Bumps [mheap/github-action-required-labels](https://github.com/mheap/github-action-required-labels) from 2 to 3. - [Release notes](https://github.com/mheap/github-action-required-labels/releases) - [Commits](https://github.com/mheap/github-action-required-labels/compare/v2...v3) --- updated-dependencies: - dependency-name: mheap/github-action-required-labels dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> --- .github/workflows/semver-require.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/semver-require.yaml b/.github/workflows/semver-require.yaml index 58cc23b54c..ee78d014d5 100644 --- a/.github/workflows/semver-require.yaml +++ b/.github/workflows/semver-require.yaml @@ -10,7 +10,7 @@ jobs: semver: runs-on: ubuntu-latest steps: - - uses: mheap/github-action-required-labels@v2 + - uses: mheap/github-action-required-labels@v3 with: mode: exactly count: 1 From 8035031fce93c9f085037411bee348ba0391d719 Mon Sep 17 00:00:00 2001 From: Emilien Macchi <emilien@redhat.com> Date: Mon, 23 Jan 2023 15:36:58 -0500 Subject: [PATCH 184/360] README: remove some notes Some notes are outdated, we don't use openlab nor vexxhost anymore, we can remove the notes here to avoid confusion on where our CI is located now. --- README.md | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/README.md b/README.md index c4fcf47533..696c2b4fde 100644 --- a/README.md +++ b/README.md @@ -138,19 +138,3 @@ See the [contributing guide](./.github/CONTRIBUTING.md). 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). - -## Thank You - -We'd like to extend special thanks and appreciation to the following: - -### OpenLab - -<a href="http://openlabtesting.org/"><img src="./docs/assets/openlab.png" width="600px"></a> - -OpenLab is providing a full CI environment to test each PR and merge for a variety of OpenStack releases. - -### VEXXHOST - -<a href="https://vexxhost.com/"><img src="./docs/assets/vexxhost.png" width="600px"></a> - -VEXXHOST is providing their services to assist with the development and testing of Gophercloud. From 14f8f68868c9ff527cc26edaf2d42c02c341a2f7 Mon Sep 17 00:00:00 2001 From: Lennart Jern <lennart.jern@est.tech> Date: Wed, 18 Jan 2023 14:40:17 +0200 Subject: [PATCH 185/360] Support value_specs for Ports --- openstack/networking/v2/ports/requests.go | 42 +++---- openstack/networking/v2/ports/results.go | 3 + .../networking/v2/ports/testing/fixtures.go | 104 ++++++++++++++++++ .../v2/ports/testing/requests_test.go | 83 ++++++++++++++ 4 files changed, 212 insertions(+), 20 deletions(-) diff --git a/openstack/networking/v2/ports/requests.go b/openstack/networking/v2/ports/requests.go index 57daf19a6f..08158b7a29 100644 --- a/openstack/networking/v2/ports/requests.go +++ b/openstack/networking/v2/ports/requests.go @@ -110,18 +110,19 @@ type CreateOptsBuilder interface { // CreateOpts represents the attributes used when creating a new port. type CreateOpts struct { - NetworkID string `json:"network_id" required:"true"` - Name string `json:"name,omitempty"` - Description string `json:"description,omitempty"` - AdminStateUp *bool `json:"admin_state_up,omitempty"` - MACAddress string `json:"mac_address,omitempty"` - FixedIPs interface{} `json:"fixed_ips,omitempty"` - DeviceID string `json:"device_id,omitempty"` - DeviceOwner string `json:"device_owner,omitempty"` - TenantID string `json:"tenant_id,omitempty"` - ProjectID string `json:"project_id,omitempty"` - SecurityGroups *[]string `json:"security_groups,omitempty"` - AllowedAddressPairs []AddressPair `json:"allowed_address_pairs,omitempty"` + NetworkID string `json:"network_id" required:"true"` + Name string `json:"name,omitempty"` + Description string `json:"description,omitempty"` + AdminStateUp *bool `json:"admin_state_up,omitempty"` + MACAddress string `json:"mac_address,omitempty"` + FixedIPs interface{} `json:"fixed_ips,omitempty"` + DeviceID string `json:"device_id,omitempty"` + DeviceOwner string `json:"device_owner,omitempty"` + TenantID string `json:"tenant_id,omitempty"` + ProjectID string `json:"project_id,omitempty"` + SecurityGroups *[]string `json:"security_groups,omitempty"` + AllowedAddressPairs []AddressPair `json:"allowed_address_pairs,omitempty"` + ValueSpecs *map[string]string `json:"value_specs,omitempty"` } // ToPortCreateMap builds a request body from CreateOpts. @@ -150,14 +151,15 @@ type UpdateOptsBuilder interface { // UpdateOpts represents the attributes used when updating an existing port. type UpdateOpts struct { - Name *string `json:"name,omitempty"` - Description *string `json:"description,omitempty"` - AdminStateUp *bool `json:"admin_state_up,omitempty"` - FixedIPs interface{} `json:"fixed_ips,omitempty"` - DeviceID *string `json:"device_id,omitempty"` - DeviceOwner *string `json:"device_owner,omitempty"` - SecurityGroups *[]string `json:"security_groups,omitempty"` - AllowedAddressPairs *[]AddressPair `json:"allowed_address_pairs,omitempty"` + Name *string `json:"name,omitempty"` + Description *string `json:"description,omitempty"` + AdminStateUp *bool `json:"admin_state_up,omitempty"` + FixedIPs interface{} `json:"fixed_ips,omitempty"` + DeviceID *string `json:"device_id,omitempty"` + DeviceOwner *string `json:"device_owner,omitempty"` + SecurityGroups *[]string `json:"security_groups,omitempty"` + AllowedAddressPairs *[]AddressPair `json:"allowed_address_pairs,omitempty"` + ValueSpecs *map[string]string `json:"value_specs,omitempty"` // RevisionNumber implements extension:standard-attr-revisions. If != "" it // will set revision_number=%s. If the revision number does not match, the diff --git a/openstack/networking/v2/ports/results.go b/openstack/networking/v2/ports/results.go index 0883534ac3..c83a743900 100644 --- a/openstack/networking/v2/ports/results.go +++ b/openstack/networking/v2/ports/results.go @@ -111,6 +111,9 @@ type Port struct { // Tags optionally set via extensions/attributestags Tags []string `json:"tags"` + // Extra parameters to include in the request. + ValueSpecs map[string]string `json:"value_specs"` + // RevisionNumber optionally set via extensions/standard-attr-revisions RevisionNumber int `json:"revision_number"` diff --git a/openstack/networking/v2/ports/testing/fixtures.go b/openstack/networking/v2/ports/testing/fixtures.go index 83dbbd8f11..d455ad809c 100644 --- a/openstack/networking/v2/ports/testing/fixtures.go +++ b/openstack/networking/v2/ports/testing/fixtures.go @@ -240,6 +240,66 @@ const CreateOmitSecurityGroupsResponse = ` } ` +const CreateValueSpecRequest = ` +{ + "port": { + "network_id": "a87cc70a-3e15-4acf-8205-9b711a3531b7", + "name": "private-port", + "admin_state_up": true, + "fixed_ips": [ + { + "subnet_id": "a0304c3a-4f08-4c43-88af-d796509c97d2", + "ip_address": "10.0.0.2" + } + ], + "security_groups": ["foo"], + "allowed_address_pairs": [ + { + "ip_address": "10.0.0.4", + "mac_address": "fa:16:3e:c9:cb:f0" + } + ], + "value_specs": { + "key": "value" + } + } +} +` + +const CreateValueSpecResponse = ` +{ + "port": { + "status": "DOWN", + "name": "private-port", + "admin_state_up": true, + "network_id": "a87cc70a-3e15-4acf-8205-9b711a3531b7", + "tenant_id": "d6700c0c9ffa4f1cb322cd4a1f3906fa", + "device_owner": "", + "mac_address": "fa:16:3e:c9:cb:f0", + "fixed_ips": [ + { + "subnet_id": "a0304c3a-4f08-4c43-88af-d796509c97d2", + "ip_address": "10.0.0.2" + } + ], + "id": "65c0ee9f-d634-4522-8954-51021b570b0d", + "security_groups": [ + "f0ac4394-7e4a-4409-9701-ba8be283dbc3" + ], + "allowed_address_pairs": [ + { + "ip_address": "10.0.0.4", + "mac_address": "fa:16:3e:c9:cb:f0" + } + ], + "value_specs": { + "key": "value" + }, + "device_id": "" + } +} +` + const CreatePortSecurityRequest = ` { "port": { @@ -401,6 +461,50 @@ const UpdateOmitSecurityGroupsResponse = ` } ` +const UpdateValueSpecsRequest = ` +{ + "port": { + "value_specs": { + "key": "value" + } + } +} +` + +const UpdateValueSpecsResponse = ` +{ + "port": { + "status": "DOWN", + "name": "new_port_name", + "admin_state_up": true, + "network_id": "a87cc70a-3e15-4acf-8205-9b711a3531b7", + "tenant_id": "d6700c0c9ffa4f1cb322cd4a1f3906fa", + "device_owner": "", + "mac_address": "fa:16:3e:c9:cb:f0", + "fixed_ips": [ + { + "subnet_id": "a0304c3a-4f08-4c43-88af-d796509c97d2", + "ip_address": "10.0.0.3" + } + ], + "allowed_address_pairs": [ + { + "ip_address": "10.0.0.4", + "mac_address": "fa:16:3e:c9:cb:f0" + } + ], + "id": "65c0ee9f-d634-4522-8954-51021b570b0d", + "security_groups": [ + "f0ac4394-7e4a-4409-9701-ba8be283dbc3" + ], + "value_specs": { + "key": "value" + }, + "device_id": "" + } +} +` + const UpdatePortSecurityRequest = ` { "port": { diff --git a/openstack/networking/v2/ports/testing/requests_test.go b/openstack/networking/v2/ports/testing/requests_test.go index 54f2ec0c51..f04d07b08f 100644 --- a/openstack/networking/v2/ports/testing/requests_test.go +++ b/openstack/networking/v2/ports/testing/requests_test.go @@ -314,6 +314,60 @@ func TestCreateWithNoSecurityGroup(t *testing.T) { }) } +func TestCreateWithValueSpecs(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/ports", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestHeader(t, r, "Content-Type", "application/json") + th.TestHeader(t, r, "Accept", "application/json") + th.TestJSONRequest(t, r, CreateValueSpecRequest) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusCreated) + + fmt.Fprintf(w, CreateValueSpecResponse) + }) + + asu := true + options := ports.CreateOpts{ + Name: "private-port", + AdminStateUp: &asu, + NetworkID: "a87cc70a-3e15-4acf-8205-9b711a3531b7", + FixedIPs: []ports.IP{ + {SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.2"}, + }, + SecurityGroups: &[]string{"foo"}, + AllowedAddressPairs: []ports.AddressPair{ + {IPAddress: "10.0.0.4", MACAddress: "fa:16:3e:c9:cb:f0"}, + }, + ValueSpecs: &map[string]string{ + "key": "value", + }, + } + n, err := ports.Create(fake.ServiceClient(), options).Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, n.Status, "DOWN") + th.AssertEquals(t, n.Name, "private-port") + th.AssertEquals(t, n.AdminStateUp, true) + th.AssertEquals(t, n.NetworkID, "a87cc70a-3e15-4acf-8205-9b711a3531b7") + th.AssertEquals(t, n.TenantID, "d6700c0c9ffa4f1cb322cd4a1f3906fa") + th.AssertEquals(t, n.DeviceOwner, "") + th.AssertEquals(t, n.MACAddress, "fa:16:3e:c9:cb:f0") + th.AssertDeepEquals(t, n.FixedIPs, []ports.IP{ + {SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.2"}, + }) + th.AssertEquals(t, n.ID, "65c0ee9f-d634-4522-8954-51021b570b0d") + th.AssertDeepEquals(t, n.SecurityGroups, []string{"f0ac4394-7e4a-4409-9701-ba8be283dbc3"}) + th.AssertDeepEquals(t, n.AllowedAddressPairs, []ports.AddressPair{ + {IPAddress: "10.0.0.4", MACAddress: "fa:16:3e:c9:cb:f0"}, + }) + th.AssertDeepEquals(t, n.ValueSpecs, map[string]string{"key": "value"}) +} + func TestRequiredCreateOpts(t *testing.T) { res := ports.Create(fake.ServiceClient(), ports.CreateOpts{}) if res.Err == nil { @@ -452,6 +506,35 @@ func TestUpdateOmitSecurityGroups(t *testing.T) { th.AssertDeepEquals(t, s.SecurityGroups, []string{"f0ac4394-7e4a-4409-9701-ba8be283dbc3"}) } +func TestUpdateValueSpecs(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/ports/65c0ee9f-d634-4522-8954-51021b570b0d", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PUT") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestHeader(t, r, "Content-Type", "application/json") + th.TestHeader(t, r, "Accept", "application/json") + th.TestJSONRequest(t, r, UpdateValueSpecsRequest) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, UpdateValueSpecsResponse) + }) + + options := ports.UpdateOpts{ + ValueSpecs: &map[string]string{ + "key": "value", + }, + } + + s, err := ports.Update(fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d", options).Extract() + th.AssertNoErr(t, err) + + th.AssertDeepEquals(t, s.ValueSpecs, map[string]string{"key": "value"}) +} + func TestUpdatePortSecurity(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() From 389e565c8b08f6a74049f15a6cbcf8cc699d1ccf Mon Sep 17 00:00:00 2001 From: Bartosz Leszczynski <bleszczynski@cloudferro.com> Date: Fri, 27 Jan 2023 13:28:39 +0100 Subject: [PATCH 186/360] Add field hidden in containerinfra/v1/clustertemplates --- .../v1/clustertemplates/requests.go | 1 + .../v1/clustertemplates/results.go | 1 + .../v1/clustertemplates/testing/fixtures.go | 22 ++++++++++++++----- .../clustertemplates/testing/requests_test.go | 1 + 4 files changed, 19 insertions(+), 6 deletions(-) diff --git a/openstack/containerinfra/v1/clustertemplates/requests.go b/openstack/containerinfra/v1/clustertemplates/requests.go index 252bdbece1..90f57558c8 100644 --- a/openstack/containerinfra/v1/clustertemplates/requests.go +++ b/openstack/containerinfra/v1/clustertemplates/requests.go @@ -38,6 +38,7 @@ type CreateOpts struct { ServerType string `json:"server_type,omitempty"` TLSDisabled *bool `json:"tls_disabled,omitempty"` VolumeDriver string `json:"volume_driver,omitempty"` + Hidden *bool `json:"hidden,omitempty"` } // ToClusterCreateMap constructs a request body from CreateOpts. diff --git a/openstack/containerinfra/v1/clustertemplates/results.go b/openstack/containerinfra/v1/clustertemplates/results.go index 8b416f7e70..1aa187b90f 100644 --- a/openstack/containerinfra/v1/clustertemplates/results.go +++ b/openstack/containerinfra/v1/clustertemplates/results.go @@ -74,6 +74,7 @@ type ClusterTemplate struct { UpdatedAt time.Time `json:"updated_at"` UserID string `json:"user_id"` VolumeDriver string `json:"volume_driver"` + Hidden bool `json:"hidden"` } // ClusterTemplatePage is the page returned by a pager when traversing over a diff --git a/openstack/containerinfra/v1/clustertemplates/testing/fixtures.go b/openstack/containerinfra/v1/clustertemplates/testing/fixtures.go index 7eca134c53..4051010d4f 100644 --- a/openstack/containerinfra/v1/clustertemplates/testing/fixtures.go +++ b/openstack/containerinfra/v1/clustertemplates/testing/fixtures.go @@ -55,7 +55,8 @@ const ClusterTemplateResponse = ` "updated_at": null, "user_id": "c48d66144e9c4a54ae2b164b85cfefe3", "uuid": "79c0f9e5-93b8-4719-8fab-063afc67bffe", - "volume_driver": "cinder" + "volume_driver": "cinder", + "hidden": false }` const ClusterTemplateResponse_EmptyTime = ` @@ -98,7 +99,8 @@ const ClusterTemplateResponse_EmptyTime = ` "tls_disabled": false, "updated_at": null, "uuid": "472807c2-f175-4946-9765-149701a5aba7", - "volume_driver": null + "volume_driver": null, + "hidden": false }` const ClusterTemplateListResponse = ` @@ -146,7 +148,8 @@ const ClusterTemplateListResponse = ` "updated_at": null, "user_id": "c48d66144e9c4a54ae2b164b85cfefe3", "uuid": "79c0f9e5-93b8-4719-8fab-063afc67bffe", - "volume_driver": "cinder" + "volume_driver": "cinder", + "hidden": false }, { "apiserver_port": null, @@ -187,7 +190,8 @@ const ClusterTemplateListResponse = ` "tls_disabled": false, "updated_at": null, "uuid": "472807c2-f175-4946-9765-149701a5aba7", - "volume_driver": null + "volume_driver": null, + "hidden": false } ] }` @@ -229,6 +233,7 @@ var ExpectedClusterTemplate = clustertemplates.ClusterTemplate{ UpdatedAt: time.Time{}, UserID: "c48d66144e9c4a54ae2b164b85cfefe3", VolumeDriver: "cinder", + Hidden: false, } var ExpectedClusterTemplate_EmptyTime = clustertemplates.ClusterTemplate{ @@ -264,6 +269,7 @@ var ExpectedClusterTemplate_EmptyTime = clustertemplates.ClusterTemplate{ UUID: "472807c2-f175-4946-9765-149701a5aba7", UpdatedAt: time.Time{}, VolumeDriver: "", + Hidden: false, } var ExpectedClusterTemplates = []clustertemplates.ClusterTemplate{ExpectedClusterTemplate, ExpectedClusterTemplate_EmptyTime} @@ -374,7 +380,8 @@ const UpdateResponse = ` "tls_disabled": false, "updated_at": null, "uuid": "472807c2-f175-4946-9765-149701a5aba7", - "volume_driver": null + "volume_driver": null, + "hidden": false }` const UpdateResponse_EmptyTime = ` @@ -417,7 +424,8 @@ const UpdateResponse_EmptyTime = ` "tls_disabled": false, "updated_at": null, "uuid": "472807c2-f175-4946-9765-149701a5aba7", - "volume_driver": null + "volume_driver": null, + "hidden": false }` const UpdateResponse_InvalidUpdate = ` @@ -458,6 +466,7 @@ var ExpectedUpdateClusterTemplate = clustertemplates.ClusterTemplate{ UUID: "472807c2-f175-4946-9765-149701a5aba7", UpdatedAt: time.Time{}, VolumeDriver: "", + Hidden: false, } var ExpectedUpdateClusterTemplate_EmptyTime = clustertemplates.ClusterTemplate{ @@ -493,6 +502,7 @@ var ExpectedUpdateClusterTemplate_EmptyTime = clustertemplates.ClusterTemplate{ UUID: "472807c2-f175-4946-9765-149701a5aba7", UpdatedAt: time.Time{}, VolumeDriver: "", + Hidden: false, } func HandleUpdateClusterTemplateSuccessfully(t *testing.T) { diff --git a/openstack/containerinfra/v1/clustertemplates/testing/requests_test.go b/openstack/containerinfra/v1/clustertemplates/testing/requests_test.go index f78f67ba38..772d128e5b 100644 --- a/openstack/containerinfra/v1/clustertemplates/testing/requests_test.go +++ b/openstack/containerinfra/v1/clustertemplates/testing/requests_test.go @@ -42,6 +42,7 @@ func TestCreateClusterTemplate(t *testing.T) { FlavorID: "m1.small", MasterLBEnabled: &boolTrue, DNSNameServer: "8.8.8.8", + Hidden: &boolTrue, } sc := fake.ServiceClient() From c5f62f9a978a522fda7141136e598ff975bcb803 Mon Sep 17 00:00:00 2001 From: Charles Vaillancourt <charles.vaillancourt@corp.ovh.com> Date: Tue, 29 Nov 2022 09:10:17 -0500 Subject: [PATCH 187/360] Modify user-agent header to ensure current gophercloud version is provided --- provider_client.go | 2 +- testing/provider_client_test.go | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/provider_client.go b/provider_client.go index cc263680c0..3c5b497d84 100644 --- a/provider_client.go +++ b/provider_client.go @@ -14,7 +14,7 @@ import ( // DefaultUserAgent is the default User-Agent string set in the request header. const ( - DefaultUserAgent = "gophercloud/2.0.0" + DefaultUserAgent = "gophercloud/v1.1.1" DefaultMaxBackoffRetries = 60 ) diff --git a/testing/provider_client_test.go b/testing/provider_client_test.go index 27210df6ae..f460bedae7 100644 --- a/testing/provider_client_test.go +++ b/testing/provider_client_test.go @@ -32,17 +32,17 @@ func TestUserAgent(t *testing.T) { p := &gophercloud.ProviderClient{} p.UserAgent.Prepend("custom-user-agent/2.4.0") - expected := "custom-user-agent/2.4.0 gophercloud/2.0.0" + expected := "custom-user-agent/2.4.0 " + gophercloud.DefaultUserAgent actual := p.UserAgent.Join() th.CheckEquals(t, expected, actual) p.UserAgent.Prepend("another-custom-user-agent/0.3.0", "a-third-ua/5.9.0") - expected = "another-custom-user-agent/0.3.0 a-third-ua/5.9.0 custom-user-agent/2.4.0 gophercloud/2.0.0" + expected = "another-custom-user-agent/0.3.0 a-third-ua/5.9.0 custom-user-agent/2.4.0 " + gophercloud.DefaultUserAgent actual = p.UserAgent.Join() th.CheckEquals(t, expected, actual) p.UserAgent = gophercloud.UserAgent{} - expected = "gophercloud/2.0.0" + expected = gophercloud.DefaultUserAgent actual = p.UserAgent.Join() th.CheckEquals(t, expected, actual) } From acd036b9fc2bfa8d9bbfb96644949ef4bf55ff2a Mon Sep 17 00:00:00 2001 From: Pierre Prinetti <pierreprinetti@redhat.com> Date: Wed, 25 Jan 2023 10:33:07 +0100 Subject: [PATCH 188/360] Convert CHANGELOG to Unix EOL --- CHANGELOG.md | 1536 +++++++++++++++++++++++++------------------------- 1 file changed, 768 insertions(+), 768 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d5ebe26269..1dde4b0d52 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,768 +1,768 @@ -## 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. +## 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. From ece82b554c6779f6f2a3f71712dad438889062dd Mon Sep 17 00:00:00 2001 From: Pierre Prinetti <pierreprinetti@redhat.com> Date: Wed, 25 Jan 2023 10:34:29 +0100 Subject: [PATCH 189/360] Prepare v1.2.0 The changelog for v1.2.0 is the output of: ``` gh pr list \ --state merged \ --search 'milestone:v1.2.0' \ --json number,title \ --template \ '{{range .}}* {{ printf "[GH-%v](https://github.com/gophercloud/gophercloud/pull/%v)" .number .number }} {{ .title }} {{end}}' ``` --- CHANGELOG.md | 9 +++++++++ provider_client.go | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1dde4b0d52..f6daa9db0e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,12 @@ +## 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.1.1`. It no longer sends the +hardcoded string `gophercloud/2.0.0`. + +* [GH-2537](https://github.com/gophercloud/gophercloud/pull/2537) Support value_specs for Ports +* [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. diff --git a/provider_client.go b/provider_client.go index 3c5b497d84..e6e80258ec 100644 --- a/provider_client.go +++ b/provider_client.go @@ -14,7 +14,7 @@ import ( // DefaultUserAgent is the default User-Agent string set in the request header. const ( - DefaultUserAgent = "gophercloud/v1.1.1" + DefaultUserAgent = "gophercloud/v1.2.0" DefaultMaxBackoffRetries = 60 ) From 253550399070cc7f0abdbc051ebb556ec947164e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= <m.andre@redhat.com> Date: Fri, 27 Jan 2023 18:26:57 +0100 Subject: [PATCH 190/360] Update changelog with latest changes The changelog for v1.2.0 is the output of: ``` gh pr list \ --state merged \ --search 'milestone:v1.2.0' \ --json number,title \ --template \ '{{range .}}* {{ printf "[GH-%v](https://github.com/gophercloud/gophercloud/pull/%v)" .number .number }} {{ .title }} {{end}}' ``` --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f6daa9db0e..63a956bf19 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,12 @@ ## 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.1.1`. It no longer sends 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) From 09f7f5e721f025d29ccf921aaf34a8fdf24cfe58 Mon Sep 17 00:00:00 2001 From: Emil Maruszczak <emil.maruszczak@thehutgroup.com> Date: Tue, 27 Dec 2022 16:26:15 +0100 Subject: [PATCH 191/360] Add get mapping operation --- .../openstack/identity/v3/federation_test.go | 10 ++++++++-- .../identity/v3/extensions/federation/doc.go | 7 +++++++ .../v3/extensions/federation/requests.go | 7 +++++++ .../identity/v3/extensions/federation/results.go | 6 ++++++ .../v3/extensions/federation/testing/fixtures.go | 16 ++++++++++++++++ .../federation/testing/requests_test.go | 10 ++++++++++ 6 files changed, 54 insertions(+), 2 deletions(-) diff --git a/acceptance/openstack/identity/v3/federation_test.go b/acceptance/openstack/identity/v3/federation_test.go index 1eae1386e1..f1fe0a63d6 100644 --- a/acceptance/openstack/identity/v3/federation_test.go +++ b/acceptance/openstack/identity/v3/federation_test.go @@ -64,7 +64,13 @@ func TestMappingsCRUD(t *testing.T) { }, } - actual, err := federation.CreateMapping(client, mappingName, createOpts).Extract() + createdMapping, err := federation.CreateMapping(client, mappingName, createOpts).Extract() th.AssertNoErr(t, err) - th.CheckDeepEquals(t, createOpts.Rules[0], actual.Rules[0]) + th.AssertEquals(t, len(createOpts.Rules), len(createdMapping.Rules)) + th.CheckDeepEquals(t, createOpts.Rules[0], createdMapping.Rules[0]) + + mapping, err := federation.GetMapping(client, mappingName).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, len(createOpts.Rules), len(mapping.Rules)) + th.CheckDeepEquals(t, createOpts.Rules[0], mapping.Rules[0]) } diff --git a/openstack/identity/v3/extensions/federation/doc.go b/openstack/identity/v3/extensions/federation/doc.go index c87eeb6609..3c1304cb7a 100644 --- a/openstack/identity/v3/extensions/federation/doc.go +++ b/openstack/identity/v3/extensions/federation/doc.go @@ -50,5 +50,12 @@ Example to Create Mappings if err != nil { panic(err) } + +Example to Get a Mapping + + mapping, err := federation.GetMapping(identityClient, "ACME").Extract() + if err != nil { + panic(err) + } */ package federation diff --git a/openstack/identity/v3/extensions/federation/requests.go b/openstack/identity/v3/extensions/federation/requests.go index f180c2282f..2c2e999b1a 100644 --- a/openstack/identity/v3/extensions/federation/requests.go +++ b/openstack/identity/v3/extensions/federation/requests.go @@ -42,3 +42,10 @@ func CreateMapping(client *gophercloud.ServiceClient, mappingID string, opts Cre _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } + +// GetMapping retrieves details on a single mapping, by ID. +func GetMapping(client *gophercloud.ServiceClient, mappingID string) (r GetMappingResult) { + resp, err := client.Get(mappingsResourceURL(client, mappingID), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} diff --git a/openstack/identity/v3/extensions/federation/results.go b/openstack/identity/v3/extensions/federation/results.go index e23a7e1499..a3262cae90 100644 --- a/openstack/identity/v3/extensions/federation/results.go +++ b/openstack/identity/v3/extensions/federation/results.go @@ -150,6 +150,12 @@ type CreateMappingResult struct { mappingResult } +// GetMappingResult is the response from a GetMapping operation. +// Call its Extract method to interpret it as a Mapping. +type GetMappingResult struct { + mappingResult +} + // MappingsPage is a single page of Mapping results. type MappingsPage struct { pagination.LinkedPageBase diff --git a/openstack/identity/v3/extensions/federation/testing/fixtures.go b/openstack/identity/v3/extensions/federation/testing/fixtures.go index 2af384fcb2..5db051fe89 100644 --- a/openstack/identity/v3/extensions/federation/testing/fixtures.go +++ b/openstack/identity/v3/extensions/federation/testing/fixtures.go @@ -130,6 +130,8 @@ const CreateOutput = ` } ` +const GetOutput = CreateOutput + var MappingACME = federation.Mapping{ ID: "ACME", Links: map[string]interface{}{ @@ -194,3 +196,17 @@ func HandleCreateMappingSuccessfully(t *testing.T) { fmt.Fprintf(w, CreateOutput) }) } + +// HandleGetMappingSuccessfully creates an HTTP handler at `/mappings` on the +// test handler mux that responds with a single mapping. +func HandleGetMappingSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/OS-FEDERATION/mappings/ACME", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "Accept", "application/json") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, GetOutput) + }) +} diff --git a/openstack/identity/v3/extensions/federation/testing/requests_test.go b/openstack/identity/v3/extensions/federation/testing/requests_test.go index d0f54ca969..9ac0f5c4c6 100644 --- a/openstack/identity/v3/extensions/federation/testing/requests_test.go +++ b/openstack/identity/v3/extensions/federation/testing/requests_test.go @@ -81,3 +81,13 @@ func TestCreateMappings(t *testing.T) { th.AssertNoErr(t, err) th.CheckDeepEquals(t, MappingACME, *actual) } + +func TestGetMapping(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleGetMappingSuccessfully(t) + + actual, err := federation.GetMapping(client.ServiceClient(), "ACME").Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, MappingACME, *actual) +} From a4db36afc2afe45c9b4200d7f97636a379e3cb1f Mon Sep 17 00:00:00 2001 From: Dmitry Tantsur <dtantsur@protonmail.com> Date: Thu, 2 Feb 2023 16:25:15 +0100 Subject: [PATCH 192/360] baremetal: add inspection_{started,finished}_at to Node --- openstack/baremetal/v1/nodes/results.go | 6 ++++++ openstack/baremetal/v1/nodes/testing/fixtures.go | 13 +++++++++---- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/openstack/baremetal/v1/nodes/results.go b/openstack/baremetal/v1/nodes/results.go index b2ec8696b0..f1996ae672 100644 --- a/openstack/baremetal/v1/nodes/results.go +++ b/openstack/baremetal/v1/nodes/results.go @@ -245,6 +245,12 @@ type Node struct { // The UTC date and time when the provision state was updated, ISO 8601 format. May be “null”. ProvisionUpdatedAt time.Time `json:"provision_updated_at"` + + // The UTC date and time when the last inspection was started, ISO 8601 format. May be “null” if inspection hasn't been started yet. + InspectionStartedAt *time.Time `json:"inspection_started_at"` + + // The UTC date and time when the last inspection was finished, ISO 8601 format. May be “null” if inspection hasn't been finished yet. + InspectionFinishedAt *time.Time `json:"inspection_finished_at"` } // NodePage abstracts the raw results of making a List() request against diff --git a/openstack/baremetal/v1/nodes/testing/fixtures.go b/openstack/baremetal/v1/nodes/testing/fixtures.go index 963a9dc234..ea9d390279 100644 --- a/openstack/baremetal/v1/nodes/testing/fixtures.go +++ b/openstack/baremetal/v1/nodes/testing/fixtures.go @@ -197,8 +197,8 @@ const NodeListDetailBody = ` "extra": {}, "fault": null, "inspect_interface": "no-inspect", - "inspection_finished_at": null, - "inspection_started_at": null, + "inspection_finished_at": "2023-02-02T14:45:59.705249Z", + "inspection_started_at": "2023-02-02T14:35:59.682403Z", "instance_info": {}, "instance_uuid": null, "last_error": null, @@ -240,7 +240,7 @@ const NodeListDetailBody = ` "power_interface": "ipmitool", "power_state": null, "properties": {}, - "provision_state": "enroll", + "provision_state": "available", "provision_updated_at": null, "raid_config": {}, "raid_interface": "no-raid", @@ -927,12 +927,15 @@ var ( "disk", } + InspectionStartedAt = time.Date(2023, time.February, 2, 14, 35, 59, 682403000, time.UTC) + InspectionFinishedAt = time.Date(2023, time.February, 2, 14, 45, 59, 705249000, time.UTC) + NodeBar = nodes.Node{ UUID: "08c84581-58f5-4ea2-a0c6-dd2e5d2b3662", Name: "bar", PowerState: "", TargetPowerState: "", - ProvisionState: "enroll", + ProvisionState: "available", TargetProvisionState: "", Maintenance: false, MaintenanceReason: "", @@ -971,6 +974,8 @@ var ( ProtectedReason: "", CreatedAt: createdAtBar, UpdatedAt: updatedAt, + InspectionStartedAt: &InspectionStartedAt, + InspectionFinishedAt: &InspectionFinishedAt, } NodeBaz = nodes.Node{ From 1ca69e694dbeeef352807ea376bc1394efcd2b67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= <m.andre@redhat.com> Date: Thu, 2 Feb 2023 18:00:45 +0100 Subject: [PATCH 193/360] Drop train job for baremetal Upstream had EOL-ed the train branch and is no longer accepting fixes. Our patch to fix the installation of virtualbmc with python 3.6 [1] was abandoned. We have no choice than dropping our train job as it's going to permafail and we can't fix it. [1] https://review.opendev.org/c/openstack/ironic/+/847436 Close #2439 --- .github/workflows/functional-baremetal.yaml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/functional-baremetal.yaml b/.github/workflows/functional-baremetal.yaml index f9f0925d75..90976bcf19 100644 --- a/.github/workflows/functional-baremetal.yaml +++ b/.github/workflows/functional-baremetal.yaml @@ -29,9 +29,6 @@ jobs: - name: "ussuri" openstack_version: "stable/ussuri" ubuntu_version: "18.04" - - name: "train" - openstack_version: "stable/train" - ubuntu_version: "18.04" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Deploy OpenStack ${{ matrix.name }} with Ironic and run baremetal acceptance tests steps: From b19861576e904b7a5f7ddd856aa01de8c39dfeb4 Mon Sep 17 00:00:00 2001 From: Pierre Prinetti <pierreprinetti@redhat.com> Date: Wed, 25 Jan 2023 12:24:52 +0100 Subject: [PATCH 194/360] Add release instructions --- README.md | 2 ++ RELEASE.md | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 RELEASE.md diff --git a/README.md b/README.md index 696c2b4fde..a39ee26e80 100644 --- a/README.md +++ b/README.md @@ -130,6 +130,8 @@ 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). diff --git a/RELEASE.md b/RELEASE.md new file mode 100644 index 0000000000..b2937448b6 --- /dev/null +++ b/RELEASE.md @@ -0,0 +1,58 @@ +# 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 and its milestone: +* make sure that the PR title is descriptive +* add a milestone based on the semver label: x++ if major, y++ if minor, z++ if patch. + +## Release of a new version + +### Step 1: Check the metadata + +Check that all pull requests merged since the last release have the right milestone. + +### Step 2: Release notes and version string + +Once all PRs have a sensible title and are added to the right milestone, generate the release notes with the [`gh`](https://github.com/cli/cli) tool: +```shell +gh pr list \ + --state merged \ + --search 'milestone:vx.y.z' \ + --json number,title \ + --template \ + '{{range .}}* {{ printf "[GH-%v](https://github.com/gophercloud/gophercloud/pull/%v)" .number .number }} {{ .title }} +{{end}}' +``` + +Replace `x.y.z` with the current milestone. + +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, and the milestone corresponding to its version. + +### 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._ From 7e05edf58f7c8dc44b5c2f888d84006d269fb624 Mon Sep 17 00:00:00 2001 From: Pierre Prinetti <pierreprinetti@redhat.com> Date: Sat, 4 Feb 2023 14:35:02 +0100 Subject: [PATCH 195/360] objects: Clarify ExtractContent usage --- openstack/objectstorage/v1/objects/requests.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openstack/objectstorage/v1/objects/requests.go b/openstack/objectstorage/v1/objects/requests.go index 63f40e9abd..9de1fec2b6 100644 --- a/openstack/objectstorage/v1/objects/requests.go +++ b/openstack/objectstorage/v1/objects/requests.go @@ -115,8 +115,8 @@ func (opts DownloadOpts) ToObjectDownloadParams() (map[string]string, string, er } // Download is a function that retrieves the content and metadata for an object. -// To extract just the content, pass the DownloadResult response to the -// ExtractContent function. +// To extract just the content, call the DownloadResult method ExtractContent, +// after checking DownloadResult's Err field. func Download(c *gophercloud.ServiceClient, containerName, objectName string, opts DownloadOptsBuilder) (r DownloadResult) { url := downloadURL(c, containerName, objectName) h := make(map[string]string) From 3d9be97a65c06ada2186d886044cf5b4378f69fe Mon Sep 17 00:00:00 2001 From: Pierre Prinetti <pierreprinetti@redhat.com> Date: Mon, 6 Feb 2023 15:04:02 +0100 Subject: [PATCH 196/360] objectstorage: Reject container names with a slash As per the [OpenStack object-storage docs](https://docs.openstack.org/api-ref/object-store/#create-container), container names must not contain a slash (`/`). With this patch, objectstorage functions error when called with a containerName containing a slash. --- .../objectstorage/v1/containers/errors.go | 13 +++ .../objectstorage/v1/containers/requests.go | 28 +++++- .../v1/containers/testing/fixtures.go | 45 +++++++-- .../v1/containers/testing/requests_test.go | 68 +++++++++++++ openstack/objectstorage/v1/containers/urls.go | 39 ++++++-- .../objectstorage/v1/objects/requests.go | 52 ++++++++-- .../v1/objects/testing/fixtures.go | 78 ++++++++++++--- .../v1/objects/testing/requests_test.go | 99 +++++++++++++++++++ openstack/objectstorage/v1/objects/urls.go | 25 +++-- pagination/pager.go | 3 + testhelper/convenience.go | 24 +++++ 11 files changed, 427 insertions(+), 47 deletions(-) create mode 100644 openstack/objectstorage/v1/containers/errors.go diff --git a/openstack/objectstorage/v1/containers/errors.go b/openstack/objectstorage/v1/containers/errors.go new file mode 100644 index 0000000000..2b99a516df --- /dev/null +++ b/openstack/objectstorage/v1/containers/errors.go @@ -0,0 +1,13 @@ +package containers + +import "github.com/gophercloud/gophercloud" + +// ErrInvalidContainerName signals a container name containing an illegal +// character. +type ErrInvalidContainerName struct { + gophercloud.BaseError +} + +func (e ErrInvalidContainerName) Error() string { + return "A container name must not contain: " + forbiddenContainerRunes +} diff --git a/openstack/objectstorage/v1/containers/requests.go b/openstack/objectstorage/v1/containers/requests.go index e5dfa4ebcc..dbfae3a061 100644 --- a/openstack/objectstorage/v1/containers/requests.go +++ b/openstack/objectstorage/v1/containers/requests.go @@ -96,6 +96,11 @@ func (opts CreateOpts) ToContainerCreateMap() (map[string]string, error) { // Create is a function that creates a new container. func Create(c *gophercloud.ServiceClient, containerName string, opts CreateOptsBuilder) (r CreateResult) { + url, err := createURL(c, containerName) + if err != nil { + r.Err = err + return + } h := make(map[string]string) if opts != nil { headers, err := opts.ToContainerCreateMap() @@ -107,7 +112,7 @@ func Create(c *gophercloud.ServiceClient, containerName string, opts CreateOptsB h[k] = v } } - resp, err := c.Request("PUT", createURL(c, containerName), &gophercloud.RequestOpts{ + resp, err := c.Request("PUT", url, &gophercloud.RequestOpts{ MoreHeaders: h, OkCodes: []int{201, 202, 204}, }) @@ -138,7 +143,12 @@ func BulkDelete(c *gophercloud.ServiceClient, containers []string) (r BulkDelete // Delete is a function that deletes a container. func Delete(c *gophercloud.ServiceClient, containerName string) (r DeleteResult) { - resp, err := c.Delete(deleteURL(c, containerName), nil) + url, err := deleteURL(c, containerName) + if err != nil { + r.Err = err + return + } + resp, err := c.Delete(url, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -189,6 +199,11 @@ func (opts UpdateOpts) ToContainerUpdateMap() (map[string]string, error) { // Update is a function that creates, updates, or deletes a container's // metadata. func Update(c *gophercloud.ServiceClient, containerName string, opts UpdateOptsBuilder) (r UpdateResult) { + url, err := updateURL(c, containerName) + if err != nil { + r.Err = err + return + } h := make(map[string]string) if opts != nil { headers, err := opts.ToContainerUpdateMap() @@ -201,7 +216,7 @@ func Update(c *gophercloud.ServiceClient, containerName string, opts UpdateOptsB h[k] = v } } - resp, err := c.Request("POST", updateURL(c, containerName), &gophercloud.RequestOpts{ + resp, err := c.Request("POST", url, &gophercloud.RequestOpts{ MoreHeaders: h, OkCodes: []int{201, 202, 204}, }) @@ -229,6 +244,11 @@ func (opts GetOpts) ToContainerGetMap() (map[string]string, error) { // the custom metadata, pass the GetResult response to the ExtractMetadata // function. func Get(c *gophercloud.ServiceClient, containerName string, opts GetOptsBuilder) (r GetResult) { + url, err := getURL(c, containerName) + if err != nil { + r.Err = err + return + } h := make(map[string]string) if opts != nil { headers, err := opts.ToContainerGetMap() @@ -241,7 +261,7 @@ func Get(c *gophercloud.ServiceClient, containerName string, opts GetOptsBuilder h[k] = v } } - resp, err := c.Head(getURL(c, containerName), &gophercloud.RequestOpts{ + resp, err := c.Head(url, &gophercloud.RequestOpts{ MoreHeaders: h, OkCodes: []int{200, 204}, }) diff --git a/openstack/objectstorage/v1/containers/testing/fixtures.go b/openstack/objectstorage/v1/containers/testing/fixtures.go index bc623b3149..1edc147bb8 100644 --- a/openstack/objectstorage/v1/containers/testing/fixtures.go +++ b/openstack/objectstorage/v1/containers/testing/fixtures.go @@ -10,6 +10,18 @@ import ( fake "github.com/gophercloud/gophercloud/testhelper/client" ) +type handlerOptions struct { + path string +} + +type option func(*handlerOptions) + +func WithPath(s string) option { + return func(h *handlerOptions) { + h.path = s + } +} + // ExpectedListInfo is the result expected from a call to `List` when full // info is requested. var ExpectedListInfo = []containers.Container{ @@ -127,8 +139,15 @@ func HandleCreateContainerSuccessfully(t *testing.T) { // HandleDeleteContainerSuccessfully creates an HTTP handler at `/testContainer` on the test handler mux that // responds with a `Delete` response. -func HandleDeleteContainerSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/testContainer", func(w http.ResponseWriter, r *http.Request) { +func HandleDeleteContainerSuccessfully(t *testing.T, options ...option) { + ho := handlerOptions{ + path: "/testContainer", + } + for _, apply := range options { + apply(&ho) + } + + th.Mux.HandleFunc(ho.path, func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Accept", "application/json") @@ -167,8 +186,15 @@ func HandleBulkDeleteSuccessfully(t *testing.T) { // HandleUpdateContainerSuccessfully creates an HTTP handler at `/testContainer` on the test handler mux that // responds with a `Update` response. -func HandleUpdateContainerSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/testContainer", func(w http.ResponseWriter, r *http.Request) { +func HandleUpdateContainerSuccessfully(t *testing.T, options ...option) { + ho := handlerOptions{ + path: "/testContainer", + } + for _, apply := range options { + apply(&ho) + } + + th.Mux.HandleFunc(ho.path, func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Accept", "application/json") @@ -183,8 +209,15 @@ func HandleUpdateContainerSuccessfully(t *testing.T) { // HandleGetContainerSuccessfully creates an HTTP handler at `/testContainer` on the test handler mux that // responds with a `Get` response. -func HandleGetContainerSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/testContainer", func(w http.ResponseWriter, r *http.Request) { +func HandleGetContainerSuccessfully(t *testing.T, options ...option) { + ho := handlerOptions{ + path: "/testContainer", + } + for _, apply := range options { + apply(&ho) + } + + th.Mux.HandleFunc(ho.path, func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "HEAD") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Accept", "application/json") diff --git a/openstack/objectstorage/v1/containers/testing/requests_test.go b/openstack/objectstorage/v1/containers/testing/requests_test.go index 3596346e63..91cca156cc 100644 --- a/openstack/objectstorage/v1/containers/testing/requests_test.go +++ b/openstack/objectstorage/v1/containers/testing/requests_test.go @@ -14,6 +14,74 @@ var ( metadata = map[string]string{"gophercloud-test": "containers"} ) +func TestContainerNames(t *testing.T) { + for _, tc := range [...]struct { + name string + containerName string + }{ + { + "rejects_a_slash", + "one/two", + }, + { + "rejects_an_escaped_slash", + "one%2Ftwo", + }, + { + "rejects_an_escaped_slash_lowercase", + "one%2ftwo", + }, + } { + t.Run(tc.name, func(t *testing.T) { + t.Run("create", func(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleCreateContainerSuccessfully(t) + + _, err := containers.Create(fake.ServiceClient(), tc.containerName, nil).Extract() + th.CheckErr(t, err, &containers.ErrInvalidContainerName{}) + }) + t.Run("delete", func(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleDeleteContainerSuccessfully(t, WithPath("/")) + + res := containers.Delete(fake.ServiceClient(), tc.containerName) + th.CheckErr(t, res.Err, &containers.ErrInvalidContainerName{}) + }) + t.Run("update", func(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleUpdateContainerSuccessfully(t, WithPath("/")) + + contentType := "text/plain" + options := &containers.UpdateOpts{ + Metadata: map[string]string{"foo": "bar"}, + ContainerWrite: new(string), + ContainerRead: new(string), + ContainerSyncTo: new(string), + ContainerSyncKey: new(string), + ContentType: &contentType, + } + res := containers.Update(fake.ServiceClient(), tc.containerName, options) + th.CheckErr(t, res.Err, &containers.ErrInvalidContainerName{}) + }) + t.Run("get", func(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleGetContainerSuccessfully(t, WithPath("/")) + + res := containers.Get(fake.ServiceClient(), tc.containerName, nil) + _, err := res.ExtractMetadata() + th.CheckErr(t, err, &containers.ErrInvalidContainerName{}) + + _, err = res.Extract() + th.CheckErr(t, err, &containers.ErrInvalidContainerName{}) + }) + }) + } +} + func TestListContainerInfo(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() diff --git a/openstack/objectstorage/v1/containers/urls.go b/openstack/objectstorage/v1/containers/urls.go index 0044a5e206..0f11a97b7f 100644 --- a/openstack/objectstorage/v1/containers/urls.go +++ b/openstack/objectstorage/v1/containers/urls.go @@ -1,24 +1,51 @@ package containers -import "github.com/gophercloud/gophercloud" +import ( + "fmt" + "strings" + + "github.com/gophercloud/gophercloud" +) + +const forbiddenContainerRunes = "/" + +func CheckContainerName(s string) error { + if strings.ContainsAny(s, forbiddenContainerRunes) { + return ErrInvalidContainerName{} + } + + // The input could (and should) already have been escaped. This cycle + // checks for the escaped versions of the forbidden characters. Note + // that a simple "contains" is sufficient, because Go's http library + // won't accept invalid escape sequences (e.g. "%%2F"). + for _, r := range forbiddenContainerRunes { + if strings.Contains(strings.ToLower(s), fmt.Sprintf("%%%x", r)) { + return ErrInvalidContainerName{} + } + } + return nil +} func listURL(c *gophercloud.ServiceClient) string { return c.Endpoint } -func createURL(c *gophercloud.ServiceClient, container string) string { - return c.ServiceURL(container) +func createURL(c *gophercloud.ServiceClient, container string) (string, error) { + if err := CheckContainerName(container); err != nil { + return "", err + } + return c.ServiceURL(container), nil } -func getURL(c *gophercloud.ServiceClient, container string) string { +func getURL(c *gophercloud.ServiceClient, container string) (string, error) { return createURL(c, container) } -func deleteURL(c *gophercloud.ServiceClient, container string) string { +func deleteURL(c *gophercloud.ServiceClient, container string) (string, error) { return createURL(c, container) } -func updateURL(c *gophercloud.ServiceClient, container string) string { +func updateURL(c *gophercloud.ServiceClient, container string) (string, error) { return createURL(c, container) } diff --git a/openstack/objectstorage/v1/objects/requests.go b/openstack/objectstorage/v1/objects/requests.go index 9de1fec2b6..639dfed472 100644 --- a/openstack/objectstorage/v1/objects/requests.go +++ b/openstack/objectstorage/v1/objects/requests.go @@ -51,9 +51,12 @@ func (opts ListOpts) ToObjectListParams() (bool, string, error) { // pass the ListResult response to the ExtractInfo or ExtractNames function, // respectively. func List(c *gophercloud.ServiceClient, containerName string, opts ListOptsBuilder) pagination.Pager { - headers := map[string]string{"Accept": "text/plain", "Content-Type": "text/plain"} + url, err := listURL(c, containerName) + if err != nil { + return pagination.Pager{Err: err} + } - url := listURL(c, containerName) + headers := map[string]string{"Accept": "text/plain", "Content-Type": "text/plain"} if opts != nil { full, query, err := opts.ToObjectListParams() if err != nil { @@ -118,7 +121,12 @@ func (opts DownloadOpts) ToObjectDownloadParams() (map[string]string, string, er // To extract just the content, call the DownloadResult method ExtractContent, // after checking DownloadResult's Err field. func Download(c *gophercloud.ServiceClient, containerName, objectName string, opts DownloadOptsBuilder) (r DownloadResult) { - url := downloadURL(c, containerName, objectName) + url, err := downloadURL(c, containerName, objectName) + if err != nil { + r.Err = err + return + } + h := make(map[string]string) if opts != nil { headers, query, err := opts.ToObjectDownloadParams() @@ -224,7 +232,11 @@ func (opts CreateOpts) ToObjectCreateParams() (io.Reader, map[string]string, str // checksum, the failed request will automatically be retried up to a maximum // of 3 times. func Create(c *gophercloud.ServiceClient, containerName, objectName string, opts CreateOptsBuilder) (r CreateResult) { - url := createURL(c, containerName, objectName) + url, err := createURL(c, containerName, objectName) + if err != nil { + r.Err = err + return + } h := make(map[string]string) var b io.Reader if opts != nil { @@ -277,18 +289,22 @@ func (opts CopyOpts) ToObjectCopyMap() (map[string]string, error) { // Copy is a function that copies one object to another. func Copy(c *gophercloud.ServiceClient, containerName, objectName string, opts CopyOptsBuilder) (r CopyResult) { + url, err := copyURL(c, containerName, objectName) + if err != nil { + r.Err = err + return + } + h := make(map[string]string) headers, err := opts.ToObjectCopyMap() if err != nil { r.Err = err return } - for k, v := range headers { h[k] = v } - url := copyURL(c, containerName, objectName) resp, err := c.Request("COPY", url, &gophercloud.RequestOpts{ MoreHeaders: h, OkCodes: []int{201}, @@ -316,7 +332,11 @@ func (opts DeleteOpts) ToObjectDeleteQuery() (string, error) { // Delete is a function that deletes an object. func Delete(c *gophercloud.ServiceClient, containerName, objectName string, opts DeleteOptsBuilder) (r DeleteResult) { - url := deleteURL(c, containerName, objectName) + url, err := deleteURL(c, containerName, objectName) + if err != nil { + r.Err = err + return + } if opts != nil { query, err := opts.ToObjectDeleteQuery() if err != nil { @@ -361,7 +381,11 @@ func (opts GetOpts) ToObjectGetParams() (map[string]string, string, error) { // the custom metadata, pass the GetResult response to the ExtractMetadata // function. func Get(c *gophercloud.ServiceClient, containerName, objectName string, opts GetOptsBuilder) (r GetResult) { - url := getURL(c, containerName, objectName) + url, err := getURL(c, containerName, objectName) + if err != nil { + r.Err = err + return + } h := make(map[string]string) if opts != nil { headers, query, err := opts.ToObjectGetParams() @@ -421,6 +445,11 @@ func (opts UpdateOpts) ToObjectUpdateMap() (map[string]string, error) { // Update is a function that creates, updates, or deletes an object's metadata. func Update(c *gophercloud.ServiceClient, containerName, objectName string, opts UpdateOptsBuilder) (r UpdateResult) { + url, err := updateURL(c, containerName, objectName) + if err != nil { + r.Err = err + return + } h := make(map[string]string) if opts != nil { headers, err := opts.ToObjectUpdateMap() @@ -433,7 +462,6 @@ func Update(c *gophercloud.ServiceClient, containerName, objectName string, opts h[k] = v } } - url := updateURL(c, containerName, objectName) resp, err := c.Post(url, nil, nil, &gophercloud.RequestOpts{ MoreHeaders: h, }) @@ -474,6 +502,11 @@ type CreateTempURLOpts struct { // allows users to have "GET" or "POST" access to a particular tenant's object // for a limited amount of time. func CreateTempURL(c *gophercloud.ServiceClient, containerName, objectName string, opts CreateTempURLOpts) (string, error) { + url, err := getURL(c, containerName, objectName) + if err != nil { + return "", err + } + if opts.Split == "" { opts.Split = "/v1/" } @@ -502,7 +535,6 @@ func CreateTempURL(c *gophercloud.ServiceClient, containerName, objectName strin tempURLKey = getHeader.TempURLKey } secretKey := []byte(tempURLKey) - url := getURL(c, containerName, objectName) splitPath := strings.Split(url, opts.Split) baseURL, objectPath := splitPath[0], splitPath[1] objectPath = opts.Split + objectPath diff --git a/openstack/objectstorage/v1/objects/testing/fixtures.go b/openstack/objectstorage/v1/objects/testing/fixtures.go index 0149d40e1e..3dbbe3706d 100644 --- a/openstack/objectstorage/v1/objects/testing/fixtures.go +++ b/openstack/objectstorage/v1/objects/testing/fixtures.go @@ -13,10 +13,29 @@ import ( fake "github.com/gophercloud/gophercloud/testhelper/client" ) +type handlerOptions struct { + path string +} + +type option func(*handlerOptions) + +func WithPath(s string) option { + return func(h *handlerOptions) { + h.path = s + } +} + // HandleDownloadObjectSuccessfully creates an HTTP handler at `/testContainer/testObject` on the test handler mux that // responds with a `Download` response. -func HandleDownloadObjectSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/testContainer/testObject", func(w http.ResponseWriter, r *http.Request) { +func HandleDownloadObjectSuccessfully(t *testing.T, options ...option) { + ho := handlerOptions{ + path: "/testContainer/testObject", + } + for _, apply := range options { + apply(&ho) + } + + th.Mux.HandleFunc(ho.path, func(w http.ResponseWriter, r *http.Request) { date := time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC) th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) @@ -79,8 +98,15 @@ var ExpectedListNames = []string{"hello", "goodbye"} // HandleListObjectsInfoSuccessfully creates an HTTP handler at `/testContainer` on the test handler mux that // responds with a `List` response when full info is requested. -func HandleListObjectsInfoSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/testContainer", func(w http.ResponseWriter, r *http.Request) { +func HandleListObjectsInfoSuccessfully(t *testing.T, options ...option) { + ho := handlerOptions{ + path: "/testContainer", + } + for _, apply := range options { + apply(&ho) + } + + th.Mux.HandleFunc(ho.path, func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Accept", "application/json") @@ -177,8 +203,15 @@ func HandleListZeroObjectNames204(t *testing.T) { // HandleCreateTextObjectSuccessfully creates an HTTP handler at `/testContainer/testObject` on the test handler mux // that responds with a `Create` response. A Content-Type of "text/plain" is expected. -func HandleCreateTextObjectSuccessfully(t *testing.T, content string) { - th.Mux.HandleFunc("/testContainer/testObject", func(w http.ResponseWriter, r *http.Request) { +func HandleCreateTextObjectSuccessfully(t *testing.T, content string, options ...option) { + ho := handlerOptions{ + path: "/testContainer/testObject", + } + for _, apply := range options { + apply(&ho) + } + + th.Mux.HandleFunc(ho.path, func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "text/plain") @@ -250,8 +283,15 @@ func HandleCopyObjectSuccessfully(t *testing.T) { // HandleDeleteObjectSuccessfully creates an HTTP handler at `/testContainer/testObject` on the test handler mux that // responds with a `Delete` response. -func HandleDeleteObjectSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/testContainer/testObject", func(w http.ResponseWriter, r *http.Request) { +func HandleDeleteObjectSuccessfully(t *testing.T, options ...option) { + ho := handlerOptions{ + path: "/testContainer/testObject", + } + for _, apply := range options { + apply(&ho) + } + + th.Mux.HandleFunc(ho.path, func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Accept", "application/json") @@ -290,8 +330,15 @@ func HandleBulkDeleteSuccessfully(t *testing.T) { // HandleUpdateObjectSuccessfully creates an HTTP handler at `/testContainer/testObject` on the test handler mux that // responds with a `Update` response. -func HandleUpdateObjectSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/testContainer/testObject", func(w http.ResponseWriter, r *http.Request) { +func HandleUpdateObjectSuccessfully(t *testing.T, options ...option) { + ho := handlerOptions{ + path: "/testContainer/testObject", + } + for _, apply := range options { + apply(&ho) + } + + th.Mux.HandleFunc(ho.path, func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Accept", "application/json") @@ -309,8 +356,15 @@ func HandleUpdateObjectSuccessfully(t *testing.T) { // HandleGetObjectSuccessfully creates an HTTP handler at `/testContainer/testObject` on the test handler mux that // responds with a `Get` response. -func HandleGetObjectSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/testContainer/testObject", func(w http.ResponseWriter, r *http.Request) { +func HandleGetObjectSuccessfully(t *testing.T, options ...option) { + ho := handlerOptions{ + path: "/testContainer/testObject", + } + for _, apply := range options { + apply(&ho) + } + + th.Mux.HandleFunc(ho.path, func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "HEAD") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Accept", "application/json") diff --git a/openstack/objectstorage/v1/objects/testing/requests_test.go b/openstack/objectstorage/v1/objects/testing/requests_test.go index ab86073182..ac89c8d244 100644 --- a/openstack/objectstorage/v1/objects/testing/requests_test.go +++ b/openstack/objectstorage/v1/objects/testing/requests_test.go @@ -12,6 +12,7 @@ import ( "time" accountTesting "github.com/gophercloud/gophercloud/openstack/objectstorage/v1/accounts/testing" + "github.com/gophercloud/gophercloud/openstack/objectstorage/v1/containers" containerTesting "github.com/gophercloud/gophercloud/openstack/objectstorage/v1/containers/testing" "github.com/gophercloud/gophercloud/openstack/objectstorage/v1/objects" "github.com/gophercloud/gophercloud/pagination" @@ -19,6 +20,104 @@ import ( fake "github.com/gophercloud/gophercloud/testhelper/client" ) +func TestContainerNames(t *testing.T) { + for _, tc := range [...]struct { + name string + containerName string + }{ + { + "rejects_a_slash", + "one/two", + }, + { + "rejects_an_escaped_slash", + "one%2Ftwo", + }, + { + "rejects_an_escaped_slash_lowercase", + "one%2ftwo", + }, + } { + t.Run(tc.name, func(t *testing.T) { + t.Run("list", func(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListObjectsInfoSuccessfully(t, WithPath("/")) + + _, err := objects.List(fake.ServiceClient(), tc.containerName, nil).AllPages() + th.CheckErr(t, err, &containers.ErrInvalidContainerName{}) + }) + t.Run("download", func(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleDownloadObjectSuccessfully(t, WithPath("/")) + + _, err := objects.Download(fake.ServiceClient(), tc.containerName, "testObject", nil).Extract() + th.CheckErr(t, err, &containers.ErrInvalidContainerName{}) + }) + t.Run("create", func(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + content := "Ceci n'est pas une pipe" + HandleCreateTextObjectSuccessfully(t, content, WithPath("/")) + + res := objects.Create(fake.ServiceClient(), tc.containerName, "testObject", &objects.CreateOpts{ + ContentType: "text/plain", + Content: strings.NewReader(content), + }) + th.CheckErr(t, res.Err, &containers.ErrInvalidContainerName{}) + }) + t.Run("delete", func(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleDeleteObjectSuccessfully(t, WithPath("/")) + + res := objects.Delete(fake.ServiceClient(), tc.containerName, "testObject", nil) + th.CheckErr(t, res.Err, &containers.ErrInvalidContainerName{}) + }) + t.Run("get", func(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleGetObjectSuccessfully(t, WithPath("/")) + + _, err := objects.Get(fake.ServiceClient(), tc.containerName, "testObject", nil).ExtractMetadata() + th.CheckErr(t, err, &containers.ErrInvalidContainerName{}) + }) + t.Run("update", func(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleUpdateObjectSuccessfully(t) + + res := objects.Update(fake.ServiceClient(), tc.containerName, "testObject", &objects.UpdateOpts{ + Metadata: map[string]string{"Gophercloud-Test": "objects"}, + }) + th.CheckErr(t, res.Err, &containers.ErrInvalidContainerName{}) + }) + t.Run("createTempURL", func(t *testing.T) { + port := 33200 + th.SetupHTTP() + th.SetupPersistentPortHTTP(t, port) + defer th.TeardownHTTP() + + // Handle fetching of secret key inside of CreateTempURL + containerTesting.HandleGetContainerSuccessfully(t) + accountTesting.HandleGetAccountSuccessfully(t) + client := fake.ServiceClient() + + // Append v1/ to client endpoint URL to be compliant with tempURL generator + client.Endpoint = client.Endpoint + "v1/" + _, err := objects.CreateTempURL(client, tc.containerName, "testObject/testFile.txt", objects.CreateTempURLOpts{ + Method: http.MethodGet, + TTL: 60, + Timestamp: time.Date(2020, 07, 01, 01, 12, 00, 00, time.UTC), + }) + + th.CheckErr(t, err, &containers.ErrInvalidContainerName{}) + }) + }) + } +} + func TestDownloadReader(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() diff --git a/openstack/objectstorage/v1/objects/urls.go b/openstack/objectstorage/v1/objects/urls.go index 918ec94b9b..172a197bfd 100644 --- a/openstack/objectstorage/v1/objects/urls.go +++ b/openstack/objectstorage/v1/objects/urls.go @@ -2,33 +2,40 @@ package objects import ( "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/objectstorage/v1/containers" ) -func listURL(c *gophercloud.ServiceClient, container string) string { - return c.ServiceURL(container) +func listURL(c *gophercloud.ServiceClient, container string) (string, error) { + if err := containers.CheckContainerName(container); err != nil { + return "", err + } + return c.ServiceURL(container), nil } -func copyURL(c *gophercloud.ServiceClient, container, object string) string { - return c.ServiceURL(container, object) +func copyURL(c *gophercloud.ServiceClient, container, object string) (string, error) { + if err := containers.CheckContainerName(container); err != nil { + return "", err + } + return c.ServiceURL(container, object), nil } -func createURL(c *gophercloud.ServiceClient, container, object string) string { +func createURL(c *gophercloud.ServiceClient, container, object string) (string, error) { return copyURL(c, container, object) } -func getURL(c *gophercloud.ServiceClient, container, object string) string { +func getURL(c *gophercloud.ServiceClient, container, object string) (string, error) { return copyURL(c, container, object) } -func deleteURL(c *gophercloud.ServiceClient, container, object string) string { +func deleteURL(c *gophercloud.ServiceClient, container, object string) (string, error) { return copyURL(c, container, object) } -func downloadURL(c *gophercloud.ServiceClient, container, object string) string { +func downloadURL(c *gophercloud.ServiceClient, container, object string) (string, error) { return copyURL(c, container, object) } -func updateURL(c *gophercloud.ServiceClient, container, object string) string { +func updateURL(c *gophercloud.ServiceClient, container, object string) (string, error) { return copyURL(c, container, object) } diff --git a/pagination/pager.go b/pagination/pager.go index 42c0b2dbe5..1dec2703eb 100644 --- a/pagination/pager.go +++ b/pagination/pager.go @@ -134,6 +134,9 @@ func (p Pager) EachPage(handler func(Page) (bool, error)) error { // AllPages returns all the pages from a `List` operation in a single page, // allowing the user to retrieve all the pages at once. func (p Pager) AllPages() (Page, error) { + if p.Err != nil { + return nil, p.Err + } // pagesSlice holds all the pages until they get converted into as Page Body. var pagesSlice []interface{} // body will contain the final concatenated Page body. diff --git a/testhelper/convenience.go b/testhelper/convenience.go index 3eb34822a7..2ae247052b 100644 --- a/testhelper/convenience.go +++ b/testhelper/convenience.go @@ -3,6 +3,7 @@ package testhelper import ( "bytes" "encoding/json" + "errors" "fmt" "path/filepath" "reflect" @@ -355,6 +356,29 @@ func CheckNoErr(t *testing.T, e error) { } } +// CheckErr is similar to AssertErr, except with a non-fatal error. If expected +// errors are passed, this function also checks that an error in e's tree is +// assignable to one of them. The tree consists of e itself, followed by the +// errors obtained by repeatedly calling Unwrap. +// +// CheckErr panics if expected contains anything other than non-nil pointers to +// either a type that implements error, or to any interface type. +func CheckErr(t *testing.T, e error, expected ...interface{}) { + if e == nil { + logError(t, "expected error, got nil") + return + } + + if len(expected) > 0 { + for _, expectedError := range expected { + if errors.As(e, expectedError) { + return + } + } + logError(t, fmt.Sprintf("unexpected error %s", yellow(e.Error()))) + } +} + // AssertIntLesserOrEqual verifies that first value is lesser or equal than second values func AssertIntLesserOrEqual(t *testing.T, v1 int, v2 int) { if !(v1 <= v2) { From c4fd26e93f7d8194cc3eedbdff448e46497f46ad Mon Sep 17 00:00:00 2001 From: kayrus <kay.diam@gmail.com> Date: Wed, 8 Feb 2023 14:41:15 +0100 Subject: [PATCH 197/360] nova: introduce servers.ListDetails along with a simple servers.List --- openstack/compute/v2/servers/doc.go | 20 +++++++++++++++++ openstack/compute/v2/servers/requests.go | 17 +++++++++++++- .../compute/v2/servers/testing/fixtures.go | 22 ++++++++++++++++++- .../v2/servers/testing/requests_test.go | 4 ++-- 4 files changed, 59 insertions(+), 4 deletions(-) diff --git a/openstack/compute/v2/servers/doc.go b/openstack/compute/v2/servers/doc.go index 3b0ab78362..bab72c1524 100644 --- a/openstack/compute/v2/servers/doc.go +++ b/openstack/compute/v2/servers/doc.go @@ -11,6 +11,26 @@ Example to List Servers 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) diff --git a/openstack/compute/v2/servers/requests.go b/openstack/compute/v2/servers/requests.go index 656e2de4d7..d6a903aab9 100644 --- a/openstack/compute/v2/servers/requests.go +++ b/openstack/compute/v2/servers/requests.go @@ -94,7 +94,22 @@ func (opts ListOpts) ToServerListQuery() (string, error) { return q.String(), err } -// List makes a request against the API to list servers accessible to you. +// 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 { diff --git a/openstack/compute/v2/servers/testing/fixtures.go b/openstack/compute/v2/servers/testing/fixtures.go index f5dd178fa2..c1761df912 100644 --- a/openstack/compute/v2/servers/testing/fixtures.go +++ b/openstack/compute/v2/servers/testing/fixtures.go @@ -893,7 +893,27 @@ func HandleServerCreationWithMetadata(t *testing.T, response string) { }) } -// HandleServerListSuccessfully sets up the test server to respond to a server List request. +// HandleServerListSimpleSuccessfully sets up the test server to respond to a server List request. +func HandleServerListSimpleSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/servers", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + w.Header().Add("Content-Type", "application/json") + r.ParseForm() + marker := r.Form.Get("marker") + switch marker { + case "": + fmt.Fprintf(w, ServerListBody) + case "9e5476bd-a4ec-4653-93d6-72c93aa682ba": + fmt.Fprintf(w, `{ "servers": [] }`) + default: + t.Fatalf("/servers invoked with unexpected marker=[%s]", marker) + } + }) +} + +// HandleServerListSuccessfully sets up the test server to respond to a server detail List request. func HandleServerListSuccessfully(t *testing.T) { th.Mux.HandleFunc("/servers/detail", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") diff --git a/openstack/compute/v2/servers/testing/requests_test.go b/openstack/compute/v2/servers/testing/requests_test.go index 2e8e58068d..5e16202cb4 100644 --- a/openstack/compute/v2/servers/testing/requests_test.go +++ b/openstack/compute/v2/servers/testing/requests_test.go @@ -49,9 +49,9 @@ func TestListServers(t *testing.T) { func TestListAllServers(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() - HandleServerListSuccessfully(t) + HandleServerListSimpleSuccessfully(t) - allPages, err := servers.List(client.ServiceClient(), servers.ListOpts{}).AllPages() + allPages, err := servers.ListSimple(client.ServiceClient(), servers.ListOpts{}).AllPages() th.AssertNoErr(t, err) actual, err := servers.ExtractServers(allPages) th.AssertNoErr(t, err) From 3d1e981de28aac1e571671f655f4a51eb7a3afdf Mon Sep 17 00:00:00 2001 From: Robert Vasek <robert.vasek@cern.ch> Date: Tue, 22 Nov 2022 17:20:10 +0100 Subject: [PATCH 198/360] Manila: add List for share-access-rules API --- .../sharedfilesystems/v2/shareaccessrules.go | 55 ++++++++++++++ .../v2/shareaccessrules_test.go | 75 ++++++++++++++++--- .../v2/shareaccessrules/requests.go | 7 ++ .../v2/shareaccessrules/results.go | 13 ++++ .../v2/shareaccessrules/testing/fixtures.go | 33 ++++++++ .../shareaccessrules/testing/requests_test.go | 14 ++++ .../v2/shareaccessrules/urls.go | 6 ++ 7 files changed, 193 insertions(+), 10 deletions(-) diff --git a/acceptance/openstack/sharedfilesystems/v2/shareaccessrules.go b/acceptance/openstack/sharedfilesystems/v2/shareaccessrules.go index 8a8c746503..2345924e8c 100644 --- a/acceptance/openstack/sharedfilesystems/v2/shareaccessrules.go +++ b/acceptance/openstack/sharedfilesystems/v2/shareaccessrules.go @@ -1,10 +1,13 @@ package v2 import ( + "fmt" "testing" "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/shareaccessrules" + "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/shares" ) func ShareAccessRuleGet(t *testing.T, client *gophercloud.ServiceClient, accessID string) (*shareaccessrules.ShareAccess, error) { @@ -16,3 +19,55 @@ func ShareAccessRuleGet(t *testing.T, client *gophercloud.ServiceClient, accessI return accessRule, nil } + +// AccessRightToShareAccess is a helper function that converts +// shares.AccessRight into shareaccessrules.ShareAccess struct. +func AccessRightToShareAccess(accessRight *shares.AccessRight) *shareaccessrules.ShareAccess { + return &shareaccessrules.ShareAccess{ + ShareID: accessRight.ShareID, + AccessType: accessRight.AccessType, + AccessTo: accessRight.AccessTo, + AccessKey: accessRight.AccessKey, + AccessLevel: accessRight.AccessLevel, + State: accessRight.State, + ID: accessRight.ID, + } +} + +func WaitForShareAccessRule(t *testing.T, client *gophercloud.ServiceClient, accessRule *shareaccessrules.ShareAccess, status string) error { + if accessRule.State == status { + return nil + } + + return tools.WaitFor(func() (bool, error) { + latest, err := ShareAccessRuleGet(t, client, accessRule.ID) + if err != nil { + if _, ok := err.(gophercloud.ErrDefault404); ok { + return false, nil + } + + return false, err + } + + if latest.State == status { + *accessRule = *latest + return true, nil + } + + if latest.State == "error" { + return false, fmt.Errorf("share access rule %s for share %s is in error state", accessRule.ID, accessRule.ShareID) + } + + return false, nil + }) +} + +func ShareAccessRuleList(t *testing.T, client *gophercloud.ServiceClient, shareID string) ([]shareaccessrules.ShareAccess, error) { + accessRules, err := shareaccessrules.List(client, shareID).Extract() + if err != nil { + t.Logf("Failed to list share access rules for share %s: %v", shareID, err) + return nil, err + } + + return accessRules, nil +} diff --git a/acceptance/openstack/sharedfilesystems/v2/shareaccessrules_test.go b/acceptance/openstack/sharedfilesystems/v2/shareaccessrules_test.go index 5da64a7252..6670c22ebe 100644 --- a/acceptance/openstack/sharedfilesystems/v2/shareaccessrules_test.go +++ b/acceptance/openstack/sharedfilesystems/v2/shareaccessrules_test.go @@ -26,23 +26,78 @@ func TestShareAccessRulesGet(t *testing.T) { defer DeleteShare(t, client, share) - shareAccessRight, err := GrantAccess(t, client, share) + addedAccessRight, err := GrantAccess(t, client, share) if err != nil { t.Fatalf("Unable to grant access to share %s: %v", share.ID, err) } - accessRule, err := ShareAccessRuleGet(t, client, shareAccessRight.ID) + addedShareAccess := AccessRightToShareAccess(addedAccessRight) + + accessRule, err := ShareAccessRuleGet(t, client, addedShareAccess.ID) if err != nil { - t.Logf("Unable to get share access rule for share %s: %v", share.ID, err) + t.Fatalf("Unable to get share access rule for share %s: %v", share.ID, err) + } + + if err = WaitForShareAccessRule(t, client, accessRule, "active"); err != nil { + t.Fatalf("Unable to wait for share access rule to achieve 'active' state: %v", err) } tools.PrintResource(t, accessRule) - th.AssertEquals(t, shareAccessRight.ID, accessRule.ID) - th.AssertEquals(t, shareAccessRight.ShareID, accessRule.ShareID) - th.AssertEquals(t, shareAccessRight.AccessType, accessRule.AccessType) - th.AssertEquals(t, shareAccessRight.AccessLevel, accessRule.AccessLevel) - th.AssertEquals(t, shareAccessRight.AccessTo, accessRule.AccessTo) - th.AssertEquals(t, shareAccessRight.AccessKey, accessRule.AccessKey) - th.AssertEquals(t, shareAccessRight.State, accessRule.State) + th.AssertEquals(t, addedShareAccess.ID, accessRule.ID) + th.AssertEquals(t, addedShareAccess.AccessType, accessRule.AccessType) + th.AssertEquals(t, addedShareAccess.AccessLevel, accessRule.AccessLevel) + th.AssertEquals(t, addedShareAccess.AccessTo, accessRule.AccessTo) + th.AssertEquals(t, addedShareAccess.AccessKey, accessRule.AccessKey) + th.AssertEquals(t, share.ID, accessRule.ShareID) + th.AssertEquals(t, "active", accessRule.State) +} + +func TestShareAccessRulesList(t *testing.T) { + client, err := clients.NewSharedFileSystemV2Client() + if err != nil { + t.Fatalf("Unable to create a shared file system client: %v", err) + } + + client.Microversion = "2.49" + + share, err := CreateShare(t, client) + if err != nil { + t.Fatalf("Unable to create a share: %v", err) + } + + defer DeleteShare(t, client, share) + + addedAccessRight, err := GrantAccess(t, client, share) + if err != nil { + t.Fatalf("Unable to grant access to share %s: %v", share.ID, err) + } + + addedShareAccess := AccessRightToShareAccess(addedAccessRight) + + if err = WaitForShareAccessRule(t, client, addedShareAccess, "active"); err != nil { + t.Fatalf("Unable to wait for share access rule to achieve 'active' state: %v", err) + } + + accessRules, err := ShareAccessRuleList(t, client, share.ID) + if err != nil { + t.Logf("Unable to list share access rules for share %s: %v", share.ID, err) + } + + tools.PrintResource(t, accessRules) + + th.AssertEquals(t, 1, len(accessRules)) + + accessRule := accessRules[0] + + if err = WaitForShareAccessRule(t, client, &accessRule, "active"); err != nil { + t.Fatalf("Unable to wait for share access rule to achieve 'active' state: %v", err) + } + + th.AssertEquals(t, addedShareAccess.ID, accessRule.ID) + th.AssertEquals(t, addedShareAccess.AccessType, accessRule.AccessType) + th.AssertEquals(t, addedShareAccess.AccessLevel, accessRule.AccessLevel) + th.AssertEquals(t, addedShareAccess.AccessTo, accessRule.AccessTo) + th.AssertEquals(t, addedShareAccess.AccessKey, accessRule.AccessKey) + th.AssertEquals(t, addedShareAccess.State, accessRule.State) } diff --git a/openstack/sharedfilesystems/v2/shareaccessrules/requests.go b/openstack/sharedfilesystems/v2/shareaccessrules/requests.go index 491085d5c1..8f96899c30 100644 --- a/openstack/sharedfilesystems/v2/shareaccessrules/requests.go +++ b/openstack/sharedfilesystems/v2/shareaccessrules/requests.go @@ -10,3 +10,10 @@ func Get(client *gophercloud.ServiceClient, accessID string) (r GetResult) { _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } + +// List gets all access rules of a share. +func List(client *gophercloud.ServiceClient, shareID string) (r ListResult) { + resp, err := client.Get(listURL(client, shareID), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} diff --git a/openstack/sharedfilesystems/v2/shareaccessrules/results.go b/openstack/sharedfilesystems/v2/shareaccessrules/results.go index 2e54f5d410..4c8a18de60 100644 --- a/openstack/sharedfilesystems/v2/shareaccessrules/results.go +++ b/openstack/sharedfilesystems/v2/shareaccessrules/results.go @@ -63,3 +63,16 @@ func (r GetResult) Extract() (*ShareAccess, error) { err := r.ExtractInto(&s) return s.ShareAccess, err } + +// ListResult contains the response body and error from a List request. +type ListResult struct { + gophercloud.Result +} + +func (r ListResult) Extract() ([]ShareAccess, error) { + var s struct { + AccessList []ShareAccess `json:"access_list"` + } + err := r.ExtractInto(&s) + return s.AccessList, err +} diff --git a/openstack/sharedfilesystems/v2/shareaccessrules/testing/fixtures.go b/openstack/sharedfilesystems/v2/shareaccessrules/testing/fixtures.go index 2f053961f9..d4b96f2e47 100644 --- a/openstack/sharedfilesystems/v2/shareaccessrules/testing/fixtures.go +++ b/openstack/sharedfilesystems/v2/shareaccessrules/testing/fixtures.go @@ -43,3 +43,36 @@ func MockGetResponse(t *testing.T) { fmt.Fprintf(w, getResponse) }) } + +var listResponse = `{ + "access_list": [ + { + "access_level": "rw", + "state": "error", + "id": "507bf114-36f2-4f56-8cf4-857985ca87c1", + "access_type": "cert", + "access_to": "example.com", + "access_key": null, + "created_at": "2018-07-17T02:01:04.000000", + "updated_at": "2018-07-17T02:01:04.000000", + "metadata": { + "key1": "value1", + "key2": "value2" + } + }, + { + "access_level": "rw", + "state": "active", + "id": "a25b2df3-90bd-4add-afa6-5f0dbbd50452", + "access_type": "ip", + "access_to": "0.0.0.0/0", + "access_key": null, + "created_at": "2018-07-16T01:03:21.000000", + "updated_at": "2018-07-16T01:03:21.000000", + "metadata": { + "key3": "value3", + "key4": "value4" + } + } + ] +}` diff --git a/openstack/sharedfilesystems/v2/shareaccessrules/testing/requests_test.go b/openstack/sharedfilesystems/v2/shareaccessrules/testing/requests_test.go index a04b5f877b..cbbfc634e5 100644 --- a/openstack/sharedfilesystems/v2/shareaccessrules/testing/requests_test.go +++ b/openstack/sharedfilesystems/v2/shareaccessrules/testing/requests_test.go @@ -1,12 +1,15 @@ package testing import ( + "fmt" + "net/http" "testing" "time" "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/shareaccessrules" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" + fake "github.com/gophercloud/gophercloud/testhelper/client" ) func TestGet(t *testing.T) { @@ -37,3 +40,14 @@ func TestGet(t *testing.T) { }, }, accessRule) } + +func MockListResponse(t *testing.T) { + th.Mux.HandleFunc(shareAccessRulesEndpoint, func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestHeader(t, r, "Accept", "application/json") + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, listResponse) + }) +} diff --git a/openstack/sharedfilesystems/v2/shareaccessrules/urls.go b/openstack/sharedfilesystems/v2/shareaccessrules/urls.go index 02766301e4..2ff1337840 100644 --- a/openstack/sharedfilesystems/v2/shareaccessrules/urls.go +++ b/openstack/sharedfilesystems/v2/shareaccessrules/urls.go @@ -1,6 +1,8 @@ package shareaccessrules import ( + "fmt" + "github.com/gophercloud/gophercloud" ) @@ -9,3 +11,7 @@ const shareAccessRulesEndpoint = "share-access-rules" func getURL(c *gophercloud.ServiceClient, accessID string) string { return c.ServiceURL(shareAccessRulesEndpoint, accessID) } + +func listURL(c *gophercloud.ServiceClient, shareID string) string { + return fmt.Sprintf("%s?share_id=%s", c.ServiceURL(shareAccessRulesEndpoint), shareID) +} From 806d4afbd8dfddc9eefb768ece8763c58b00d38a Mon Sep 17 00:00:00 2001 From: Stephen Finucane <stephenfin@redhat.com> Date: Thu, 16 Feb 2023 11:16:20 +0000 Subject: [PATCH 199/360] Expand docs on 'clientconfig' usage Even though this is a separate package, it's helpful to include some minimal documentation for same here rather than insisting folks root through the gophercloud/utils docs. Signed-off-by: Stephen Finucane <stephenfin@redhat.com> --- README.md | 118 ++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 75 insertions(+), 43 deletions(-) diff --git a/README.md b/README.md index a39ee26e80..0e3e13a049 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ Gophercloud is an OpenStack Go SDK. Reference a Gophercloud package in your code: -```Go +```go import "github.com/gophercloud/gophercloud" ``` @@ -28,43 +28,80 @@ go mod tidy ### Credentials Because you'll be hitting an API, you will need to retrieve your OpenStack -credentials and either store them 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. +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: -* username -* password -* a valid Keystone identity URL +* 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 that have the OpenStack dashboard installed, there's a shortcut. If -you visit the `project/access_and_security` path in Horizon and click on the -"Download OpenStack RC File" button at the top right hand corner, you will -download a bash file that exports all of your access details to environment -variables. To execute the file, run `source admin-openrc.sh` and you will be -prompted for your password. +you visit the `project/api_access` path in Horizon and click on the +"Download OpenStack RC File" button at the top right hand corner, you can +download either a `clouds.yaml` file or an `openrc` bash file that exports all +of your access details to environment variables. To use the `clouds.yaml` file, +place it at `~/.config/openstack/clouds.yaml`. To use the `openrc` file, run +`source openrc` and you will be prompted for your password. ### Authentication -> NOTE: It is now recommended to use the `clientconfig` package found at -> https://github.com/gophercloud/utils/tree/master/openstack/clientconfig -> for all authentication purposes. -> -> The below documentation is still relevant. clientconfig simply implements -> the below and presents it in an easier and more flexible way. - Once you have access to your credentials, you can begin plugging them into -Gophercloud. The next step is authentication, and this is handled by a base -"Provider" struct. To get one, you can either pass in your credentials -explicitly, or tell Gophercloud to use environment variables: +Gophercloud. The next step is authentication, which is handled by a base +"Provider" struct. There are number of ways to construct such a struct. + +**With `gophercloud/utils`** + +The [github.com/gophercloud/utils](https://github.com/gophercloud/utils) +library provides the `clientconfig` package to simplify authentication. It +provides additional functionality, such as the ability to read `clouds.yaml` +files. To generate a "Provider" struct using the `clientconfig` package: + +```go +import ( + "github.com/gophercloud/utils/openstack/clientconfig" +) + +// You can also skip configuring this and instead set 'OS_CLOUD' in your +// environment +opts := new(clientconfig.ClientOpts) +opts.Cloud = "devstack-admin" + +provider, err := clientconfig.AuthenticatedClient(opts) +``` + +A provider client is a top-level client that all of your OpenStack service +clients derive from. The provider contains all of the authentication details +that allow your Go code to access the API - such as the base URL and token ID. + +Once we have a base Provider, we inject it as a dependency into each OpenStack +service. For example, in order to work with the Compute API, we need a Compute +service client. This can be created like so: + +```go +client, err := clientconfig.NewServiceClient("compute", opts) +``` + +**Without `gophercloud/utils`** + +> *Note* +> gophercloud doesn't provide support for `clouds.yaml` file so you need to +> implement this functionality yourself if you don't wish to use +> `gophercloud/utils`. + +You can also generate a "Provider" struct without using the `clientconfig` +package from `gophercloud/utils`. To do this, you can either pass in your +credentials explicitly or tell Gophercloud to use environment variables: ```go import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack" - "github.com/gophercloud/gophercloud/openstack/utils" + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack" + "github.com/gophercloud/gophercloud/openstack/utils" ) // Option 1: Pass in the values yourself @@ -85,34 +122,29 @@ Once you have the `opts` variable, you can pass it in and get back a provider, err := openstack.AuthenticatedClient(opts) ``` -The `ProviderClient` is the top-level client that all of your OpenStack services -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. - -### Provision a server - -Once we have a base Provider, we inject it as a dependency into each OpenStack -service. In order to work with the Compute API, we need a Compute service -client; which can be created like so: +As above, you can then use this provider client to generate a service client +for a particular OpenStack service: ```go client, err := openstack.NewComputeV2(provider, gophercloud.EndpointOpts{ - Region: os.Getenv("OS_REGION_NAME"), + Region: os.Getenv("OS_REGION_NAME"), }) ``` -We then use this `client` for any Compute API operation we want. In our case, -we want to provision a new server - so we invoke the `Create` method and pass -in the flavor ID (hardware specification) and image ID (operating system) we're -interested in: +### Provision a server + +We can use the Compute service client generated above for any Compute API +operation we want. In our case, we want to provision a new server. To do this, +we invoke the `Create` method and pass in the flavor ID (hardware +specification) and image ID (operating system) we're interested in: ```go import "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" server, err := servers.Create(client, servers.CreateOpts{ - Name: "My new server!", - FlavorRef: "flavor_id", - ImageRef: "image_id", + Name: "My new server!", + FlavorRef: "flavor_id", + ImageRef: "image_id", }).Extract() ``` From 4b279cf2476fc273e1072e4f212f8cd92ab83332 Mon Sep 17 00:00:00 2001 From: Lennart Jern <lennart.jern@est.tech> Date: Fri, 17 Feb 2023 09:30:22 +0200 Subject: [PATCH 200/360] Support propagate_uplink_status for Ports --- openstack/networking/v2/ports/requests.go | 46 ++++++----- openstack/networking/v2/ports/results.go | 3 + .../networking/v2/ports/testing/fixtures.go | 80 +++++++++++++++++++ .../v2/ports/testing/requests_test.go | 73 +++++++++++++++++ 4 files changed, 180 insertions(+), 22 deletions(-) diff --git a/openstack/networking/v2/ports/requests.go b/openstack/networking/v2/ports/requests.go index 08158b7a29..48f9985643 100644 --- a/openstack/networking/v2/ports/requests.go +++ b/openstack/networking/v2/ports/requests.go @@ -110,19 +110,20 @@ type CreateOptsBuilder interface { // CreateOpts represents the attributes used when creating a new port. type CreateOpts struct { - NetworkID string `json:"network_id" required:"true"` - Name string `json:"name,omitempty"` - Description string `json:"description,omitempty"` - AdminStateUp *bool `json:"admin_state_up,omitempty"` - MACAddress string `json:"mac_address,omitempty"` - FixedIPs interface{} `json:"fixed_ips,omitempty"` - DeviceID string `json:"device_id,omitempty"` - DeviceOwner string `json:"device_owner,omitempty"` - TenantID string `json:"tenant_id,omitempty"` - ProjectID string `json:"project_id,omitempty"` - SecurityGroups *[]string `json:"security_groups,omitempty"` - AllowedAddressPairs []AddressPair `json:"allowed_address_pairs,omitempty"` - ValueSpecs *map[string]string `json:"value_specs,omitempty"` + NetworkID string `json:"network_id" required:"true"` + Name string `json:"name,omitempty"` + Description string `json:"description,omitempty"` + AdminStateUp *bool `json:"admin_state_up,omitempty"` + MACAddress string `json:"mac_address,omitempty"` + FixedIPs interface{} `json:"fixed_ips,omitempty"` + DeviceID string `json:"device_id,omitempty"` + DeviceOwner string `json:"device_owner,omitempty"` + TenantID string `json:"tenant_id,omitempty"` + ProjectID string `json:"project_id,omitempty"` + SecurityGroups *[]string `json:"security_groups,omitempty"` + AllowedAddressPairs []AddressPair `json:"allowed_address_pairs,omitempty"` + PropagateUplinkStatus *bool `json:"propagate_uplink_status,omitempty"` + ValueSpecs *map[string]string `json:"value_specs,omitempty"` } // ToPortCreateMap builds a request body from CreateOpts. @@ -151,15 +152,16 @@ type UpdateOptsBuilder interface { // UpdateOpts represents the attributes used when updating an existing port. type UpdateOpts struct { - Name *string `json:"name,omitempty"` - Description *string `json:"description,omitempty"` - AdminStateUp *bool `json:"admin_state_up,omitempty"` - FixedIPs interface{} `json:"fixed_ips,omitempty"` - DeviceID *string `json:"device_id,omitempty"` - DeviceOwner *string `json:"device_owner,omitempty"` - SecurityGroups *[]string `json:"security_groups,omitempty"` - AllowedAddressPairs *[]AddressPair `json:"allowed_address_pairs,omitempty"` - ValueSpecs *map[string]string `json:"value_specs,omitempty"` + Name *string `json:"name,omitempty"` + Description *string `json:"description,omitempty"` + AdminStateUp *bool `json:"admin_state_up,omitempty"` + FixedIPs interface{} `json:"fixed_ips,omitempty"` + DeviceID *string `json:"device_id,omitempty"` + DeviceOwner *string `json:"device_owner,omitempty"` + SecurityGroups *[]string `json:"security_groups,omitempty"` + AllowedAddressPairs *[]AddressPair `json:"allowed_address_pairs,omitempty"` + PropagateUplinkStatus *bool `json:"propagate_uplink_status,omitempty"` + ValueSpecs *map[string]string `json:"value_specs,omitempty"` // RevisionNumber implements extension:standard-attr-revisions. If != "" it // will set revision_number=%s. If the revision number does not match, the diff --git a/openstack/networking/v2/ports/results.go b/openstack/networking/v2/ports/results.go index c83a743900..cf580bbc19 100644 --- a/openstack/networking/v2/ports/results.go +++ b/openstack/networking/v2/ports/results.go @@ -111,6 +111,9 @@ type Port struct { // Tags optionally set via extensions/attributestags Tags []string `json:"tags"` + // PropagateUplinkStatus enables/disables propagate uplink status on the port. + PropagateUplinkStatus bool `json:"propagate_uplink_status"` + // Extra parameters to include in the request. ValueSpecs map[string]string `json:"value_specs"` diff --git a/openstack/networking/v2/ports/testing/fixtures.go b/openstack/networking/v2/ports/testing/fixtures.go index d455ad809c..a54cf39622 100644 --- a/openstack/networking/v2/ports/testing/fixtures.go +++ b/openstack/networking/v2/ports/testing/fixtures.go @@ -240,6 +240,46 @@ const CreateOmitSecurityGroupsResponse = ` } ` +const CreatePropagateUplinkStatusRequest = ` +{ + "port": { + "network_id": "a87cc70a-3e15-4acf-8205-9b711a3531b7", + "name": "private-port", + "admin_state_up": true, + "fixed_ips": [ + { + "subnet_id": "a0304c3a-4f08-4c43-88af-d796509c97d2", + "ip_address": "10.0.0.2" + } + ], + "propagate_uplink_status": true + } +} +` + +const CreatePropagateUplinkStatusResponse = ` +{ + "port": { + "status": "DOWN", + "name": "private-port", + "admin_state_up": true, + "network_id": "a87cc70a-3e15-4acf-8205-9b711a3531b7", + "tenant_id": "d6700c0c9ffa4f1cb322cd4a1f3906fa", + "device_owner": "", + "mac_address": "fa:16:3e:c9:cb:f0", + "fixed_ips": [ + { + "subnet_id": "a0304c3a-4f08-4c43-88af-d796509c97d2", + "ip_address": "10.0.0.2" + } + ], + "id": "65c0ee9f-d634-4522-8954-51021b570b0d", + "propagate_uplink_status": true, + "device_id": "" + } +} +` + const CreateValueSpecRequest = ` { "port": { @@ -461,6 +501,46 @@ const UpdateOmitSecurityGroupsResponse = ` } ` +const UpdatePropagateUplinkStatusRequest = ` +{ + "port": { + "propagate_uplink_status": true + } +} +` + +const UpdatePropagateUplinkStatusResponse = ` +{ + "port": { + "status": "DOWN", + "name": "new_port_name", + "admin_state_up": true, + "network_id": "a87cc70a-3e15-4acf-8205-9b711a3531b7", + "tenant_id": "d6700c0c9ffa4f1cb322cd4a1f3906fa", + "device_owner": "", + "mac_address": "fa:16:3e:c9:cb:f0", + "fixed_ips": [ + { + "subnet_id": "a0304c3a-4f08-4c43-88af-d796509c97d2", + "ip_address": "10.0.0.3" + } + ], + "allowed_address_pairs": [ + { + "ip_address": "10.0.0.4", + "mac_address": "fa:16:3e:c9:cb:f0" + } + ], + "id": "65c0ee9f-d634-4522-8954-51021b570b0d", + "security_groups": [ + "f0ac4394-7e4a-4409-9701-ba8be283dbc3" + ], + "propagate_uplink_status": true, + "device_id": "" + } +} +` + const UpdateValueSpecsRequest = ` { "port": { diff --git a/openstack/networking/v2/ports/testing/requests_test.go b/openstack/networking/v2/ports/testing/requests_test.go index f04d07b08f..5113f005fd 100644 --- a/openstack/networking/v2/ports/testing/requests_test.go +++ b/openstack/networking/v2/ports/testing/requests_test.go @@ -314,6 +314,51 @@ func TestCreateWithNoSecurityGroup(t *testing.T) { }) } +func TestCreateWithPropagateUplinkStatus(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/ports", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestHeader(t, r, "Content-Type", "application/json") + th.TestHeader(t, r, "Accept", "application/json") + th.TestJSONRequest(t, r, CreatePropagateUplinkStatusRequest) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusCreated) + + fmt.Fprintf(w, CreatePropagateUplinkStatusResponse) + }) + + asu := true + propagateUplinkStatus := true + options := ports.CreateOpts{ + Name: "private-port", + AdminStateUp: &asu, + NetworkID: "a87cc70a-3e15-4acf-8205-9b711a3531b7", + FixedIPs: []ports.IP{ + {SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.2"}, + }, + PropagateUplinkStatus: &propagateUplinkStatus, + } + n, err := ports.Create(fake.ServiceClient(), options).Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, n.Status, "DOWN") + th.AssertEquals(t, n.Name, "private-port") + th.AssertEquals(t, n.AdminStateUp, true) + th.AssertEquals(t, n.NetworkID, "a87cc70a-3e15-4acf-8205-9b711a3531b7") + th.AssertEquals(t, n.TenantID, "d6700c0c9ffa4f1cb322cd4a1f3906fa") + th.AssertEquals(t, n.DeviceOwner, "") + th.AssertEquals(t, n.MACAddress, "fa:16:3e:c9:cb:f0") + th.AssertDeepEquals(t, n.FixedIPs, []ports.IP{ + {SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.2"}, + }) + th.AssertEquals(t, n.ID, "65c0ee9f-d634-4522-8954-51021b570b0d") + th.AssertEquals(t, n.PropagateUplinkStatus, propagateUplinkStatus) +} + func TestCreateWithValueSpecs(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() @@ -506,6 +551,34 @@ func TestUpdateOmitSecurityGroups(t *testing.T) { th.AssertDeepEquals(t, s.SecurityGroups, []string{"f0ac4394-7e4a-4409-9701-ba8be283dbc3"}) } +func TestUpdatePropagateUplinkStatus(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/ports/65c0ee9f-d634-4522-8954-51021b570b0d", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PUT") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestHeader(t, r, "Content-Type", "application/json") + th.TestHeader(t, r, "Accept", "application/json") + th.TestJSONRequest(t, r, UpdatePropagateUplinkStatusRequest) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, UpdatePropagateUplinkStatusResponse) + }) + + propagateUplinkStatus := true + options := ports.UpdateOpts{ + PropagateUplinkStatus: &propagateUplinkStatus, + } + + s, err := ports.Update(fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d", options).Extract() + th.AssertNoErr(t, err) + + th.AssertDeepEquals(t, s.PropagateUplinkStatus, propagateUplinkStatus) +} + func TestUpdateValueSpecs(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() From 752e65439f0182f5fee8c89ed54b3b5325640dc2 Mon Sep 17 00:00:00 2001 From: Stephen Finucane <stephenfin@redhat.com> Date: Mon, 20 Feb 2023 10:10:52 +0000 Subject: [PATCH 201/360] README: Remove unnecessary import Signed-off-by: Stephen Finucane <stephenfin@redhat.com> --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 0e3e13a049..89b08156fe 100644 --- a/README.md +++ b/README.md @@ -101,7 +101,6 @@ credentials explicitly or tell Gophercloud to use environment variables: import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack" - "github.com/gophercloud/gophercloud/openstack/utils" ) // Option 1: Pass in the values yourself From 4d6ab6bc893fd0d5c85c2e8e3a7e0218dc0ae107 Mon Sep 17 00:00:00 2001 From: Dmitry Tantsur <dtantsur@protonmail.com> Date: Thu, 23 Feb 2023 17:15:52 +0100 Subject: [PATCH 202/360] Fix invalid baremetal-introspection service type --- openstack/baremetalintrospection/httpbasic/requests.go | 2 +- openstack/baremetalintrospection/noauth/requests.go | 2 +- openstack/client.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/openstack/baremetalintrospection/httpbasic/requests.go b/openstack/baremetalintrospection/httpbasic/requests.go index 0a726aa1f0..587eb39570 100644 --- a/openstack/baremetalintrospection/httpbasic/requests.go +++ b/openstack/baremetalintrospection/httpbasic/requests.go @@ -39,7 +39,7 @@ func NewBareMetalIntrospectionHTTPBasic(eo EndpointOpts) (*gophercloud.ServiceCl return nil, err } - sc.Type = "baremetal-inspector" + sc.Type = "baremetal-introspection" return sc, nil } diff --git a/openstack/baremetalintrospection/noauth/requests.go b/openstack/baremetalintrospection/noauth/requests.go index 97816cdf92..a528e1030c 100644 --- a/openstack/baremetalintrospection/noauth/requests.go +++ b/openstack/baremetalintrospection/noauth/requests.go @@ -33,7 +33,7 @@ func NewBareMetalIntrospectionNoAuth(eo EndpointOpts) (*gophercloud.ServiceClien return nil, err } - sc.Type = "baremetal-inspector" + sc.Type = "baremetal-introspection" return sc, nil } diff --git a/openstack/client.go b/openstack/client.go index 655a9f6b91..81c907c35b 100644 --- a/openstack/client.go +++ b/openstack/client.go @@ -369,7 +369,7 @@ func NewBareMetalV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointO // NewBareMetalIntrospectionV1 creates a ServiceClient that may be used with the v1 // bare metal introspection package. func NewBareMetalIntrospectionV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { - return initClientOpts(client, eo, "baremetal-inspector") + return initClientOpts(client, eo, "baremetal-introspection") } // NewObjectStorageV1 creates a ServiceClient that may be used with the v1 From a29b7d7b0cd00cb91ad5f2df5207fee1be47ad97 Mon Sep 17 00:00:00 2001 From: kayrus <kay.diam@gmail.com> Date: Fri, 24 Feb 2023 22:13:45 +0100 Subject: [PATCH 203/360] networking v2: add extraroutes Add and Remove methods --- .../v2/extensions/layer3/extraroutes_test.go | 108 +++++++++++++ .../extensions/layer3/extraroutes/requests.go | 51 ++++++ .../extensions/layer3/extraroutes/results.go | 31 ++++ .../layer3/extraroutes/testing/doc.go | 2 + .../extraroutes/testing/requests_test.go | 150 ++++++++++++++++++ .../v2/extensions/layer3/extraroutes/urls.go | 13 ++ 6 files changed, 355 insertions(+) create mode 100644 acceptance/openstack/networking/v2/extensions/layer3/extraroutes_test.go create mode 100644 openstack/networking/v2/extensions/layer3/extraroutes/requests.go create mode 100644 openstack/networking/v2/extensions/layer3/extraroutes/results.go create mode 100644 openstack/networking/v2/extensions/layer3/extraroutes/testing/doc.go create mode 100644 openstack/networking/v2/extensions/layer3/extraroutes/testing/requests_test.go create mode 100644 openstack/networking/v2/extensions/layer3/extraroutes/urls.go diff --git a/acceptance/openstack/networking/v2/extensions/layer3/extraroutes_test.go b/acceptance/openstack/networking/v2/extensions/layer3/extraroutes_test.go new file mode 100644 index 0000000000..b5b4c9180e --- /dev/null +++ b/acceptance/openstack/networking/v2/extensions/layer3/extraroutes_test.go @@ -0,0 +1,108 @@ +//go:build acceptance || networking || layer3 || router +// +build acceptance networking layer3 router + +package layer3 + +import ( + "fmt" + "net" + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + networking "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/extraroutes" + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/routers" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestLayer3ExtraRoutesAddRemove(t *testing.T) { + client, err := clients.NewNetworkV2Client() + th.AssertNoErr(t, err) + + network, err := networking.CreateNetwork(t, client) + th.AssertNoErr(t, err) + defer networking.DeleteNetwork(t, client, network.ID) + + subnet, err := networking.CreateSubnet(t, client, network.ID) + th.AssertNoErr(t, err) + defer networking.DeleteSubnet(t, client, subnet.ID) + tmp := net.ParseIP(subnet.GatewayIP).To4() + if tmp == nil { + th.AssertNoErr(t, fmt.Errorf("invalid subnet gateway IP: %s", subnet.GatewayIP)) + } + tmp[3] = 251 + gateway := tmp.String() + + router, err := CreateRouter(t, client, network.ID) + th.AssertNoErr(t, err) + defer DeleteRouter(t, client, router.ID) + + tools.PrintResource(t, router) + + aiOpts := routers.AddInterfaceOpts{ + SubnetID: subnet.ID, + } + iface, err := routers.AddInterface(client, router.ID, aiOpts).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, iface) + + // 2. delete router interface + defer func() { + riOpts := routers.RemoveInterfaceOpts{ + SubnetID: subnet.ID, + } + _, err = routers.RemoveInterface(client, router.ID, riOpts).Extract() + th.AssertNoErr(t, err) + }() + + // 1. delete routes first + defer func() { + routes := []routers.Route{} + opts := routers.UpdateOpts{ + Routes: &routes, + } + _, err = routers.Update(client, router.ID, opts).Extract() + th.AssertNoErr(t, err) + }() + + routes := []routers.Route{ + { + DestinationCIDR: "192.168.11.0/30", + NextHop: gateway, + }, + { + DestinationCIDR: "192.168.12.0/30", + NextHop: gateway, + }, + } + updateOpts := routers.UpdateOpts{ + Routes: &routes, + } + _, err = routers.Update(client, router.ID, updateOpts).Extract() + th.AssertNoErr(t, err) + + newRoutes := []routers.Route{ + { + DestinationCIDR: "192.168.13.0/30", + NextHop: gateway, + }, + { + DestinationCIDR: "192.168.14.0/30", + NextHop: gateway, + }, + } + opts := extraroutes.Opts{ + Routes: &newRoutes, + } + // add new routes + rt, err := extraroutes.Add(client, router.ID, opts).Extract() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, append(routes, newRoutes...), rt.Routes) + + // remove new routes + rt, err = extraroutes.Remove(client, router.ID, opts).Extract() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, routes, rt.Routes) +} diff --git a/openstack/networking/v2/extensions/layer3/extraroutes/requests.go b/openstack/networking/v2/extensions/layer3/extraroutes/requests.go new file mode 100644 index 0000000000..6049f9c48e --- /dev/null +++ b/openstack/networking/v2/extensions/layer3/extraroutes/requests.go @@ -0,0 +1,51 @@ +package extraroutes + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/routers" +) + +// OptsBuilder allows extensions to add additional parameters to the Add or +// Remove requests. +type OptsBuilder interface { + ToExtraRoutesUpdateMap() (map[string]interface{}, error) +} + +// Opts contains the values needed to add or remove a list og routes on a +// router. +type Opts struct { + Routes *[]routers.Route `json:"routes,omitempty"` +} + +// ToExtraRoutesUpdateMap builds a body based on Opts. +func (opts Opts) ToExtraRoutesUpdateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "router") +} + +// Add allows routers to be updated with a list of routes to be added. +func Add(c *gophercloud.ServiceClient, id string, opts OptsBuilder) (r AddResult) { + b, err := opts.ToExtraRoutesUpdateMap() + if err != nil { + r.Err = err + return + } + resp, err := c.Put(addExtraRoutesURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// Remove allows routers to be updated with a list of routes to be removed. +func Remove(c *gophercloud.ServiceClient, id string, opts OptsBuilder) (r RemoveResult) { + b, err := opts.ToExtraRoutesUpdateMap() + if err != nil { + r.Err = err + return + } + resp, err := c.Put(removeExtraRoutesURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} diff --git a/openstack/networking/v2/extensions/layer3/extraroutes/results.go b/openstack/networking/v2/extensions/layer3/extraroutes/results.go new file mode 100644 index 0000000000..522aa8e85f --- /dev/null +++ b/openstack/networking/v2/extensions/layer3/extraroutes/results.go @@ -0,0 +1,31 @@ +package extraroutes + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/routers" +) + +// Extract is a function that accepts a result and extracts a router. +func (r commonResult) Extract() (*routers.Router, error) { + var s struct { + Router *routers.Router `json:"router"` + } + err := r.ExtractInto(&s) + return s.Router, err +} + +type commonResult struct { + gophercloud.Result +} + +// AddResult represents the result of an extra routes add operation. Call its +// Extract method to interpret it as a *routers.Router. +type AddResult struct { + commonResult +} + +// RemoveResult represents the result of an extra routes remove operation. Call +// its Extract method to interpret it as a *routers.Router. +type RemoveResult struct { + commonResult +} diff --git a/openstack/networking/v2/extensions/layer3/extraroutes/testing/doc.go b/openstack/networking/v2/extensions/layer3/extraroutes/testing/doc.go new file mode 100644 index 0000000000..68137fab47 --- /dev/null +++ b/openstack/networking/v2/extensions/layer3/extraroutes/testing/doc.go @@ -0,0 +1,2 @@ +// extraroutes unit tests +package testing diff --git a/openstack/networking/v2/extensions/layer3/extraroutes/testing/requests_test.go b/openstack/networking/v2/extensions/layer3/extraroutes/testing/requests_test.go new file mode 100644 index 0000000000..070e8f29b2 --- /dev/null +++ b/openstack/networking/v2/extensions/layer3/extraroutes/testing/requests_test.go @@ -0,0 +1,150 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/extraroutes" + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/routers" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestAddExtraRoutes(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/routers/4e8e5957-649f-477b-9e5b-f1f75b21c03c/add_extraroutes", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PUT") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestHeader(t, r, "Content-Type", "application/json") + th.TestHeader(t, r, "Accept", "application/json") + th.TestJSONRequest(t, r, ` +{ + "router": { + "routes": [ + { "destination" : "10.0.3.0/24", "nexthop" : "10.0.0.13" }, + { "destination" : "10.0.4.0/24", "nexthop" : "10.0.0.14" } + ] + } +} + `) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, ` +{ + "router": { + "name": "name", + "id": "8604a0de-7f6b-409a-a47c-a1cc7bc77b2e", + "routes": [ + { "destination" : "10.0.1.0/24", "nexthop" : "10.0.0.11" }, + { "destination" : "10.0.2.0/24", "nexthop" : "10.0.0.12" }, + { "destination" : "10.0.3.0/24", "nexthop" : "10.0.0.13" }, + { "destination" : "10.0.4.0/24", "nexthop" : "10.0.0.14" } + ] + } +} + `) + }) + + r := []routers.Route{ + { + DestinationCIDR: "10.0.3.0/24", + NextHop: "10.0.0.13", + }, + { + DestinationCIDR: "10.0.4.0/24", + NextHop: "10.0.0.14", + }, + } + options := extraroutes.Opts{Routes: &r} + + n, err := extraroutes.Add(fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c", options).Extract() + th.AssertNoErr(t, err) + + th.AssertDeepEquals(t, n.Routes, []routers.Route{ + { + DestinationCIDR: "10.0.1.0/24", + NextHop: "10.0.0.11", + }, + { + DestinationCIDR: "10.0.2.0/24", + NextHop: "10.0.0.12", + }, + { + DestinationCIDR: "10.0.3.0/24", + NextHop: "10.0.0.13", + }, + { + DestinationCIDR: "10.0.4.0/24", + NextHop: "10.0.0.14", + }, + }) +} + +func TestRemoveExtraRoutes(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/routers/4e8e5957-649f-477b-9e5b-f1f75b21c03c/remove_extraroutes", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PUT") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestHeader(t, r, "Content-Type", "application/json") + th.TestHeader(t, r, "Accept", "application/json") + th.TestJSONRequest(t, r, ` +{ + "router": { + "routes": [ + { "destination" : "10.0.3.0/24", "nexthop" : "10.0.0.13" }, + { "destination" : "10.0.4.0/24", "nexthop" : "10.0.0.14" } + ] + } +} + `) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, ` +{ + "router": { + "name": "name", + "id": "8604a0de-7f6b-409a-a47c-a1cc7bc77b2e", + "routes": [ + { "destination" : "10.0.1.0/24", "nexthop" : "10.0.0.11" }, + { "destination" : "10.0.2.0/24", "nexthop" : "10.0.0.12" } + ] + } +} + `) + }) + + r := []routers.Route{ + { + DestinationCIDR: "10.0.3.0/24", + NextHop: "10.0.0.13", + }, + { + DestinationCIDR: "10.0.4.0/24", + NextHop: "10.0.0.14", + }, + } + options := extraroutes.Opts{Routes: &r} + + n, err := extraroutes.Remove(fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c", options).Extract() + th.AssertNoErr(t, err) + + th.AssertDeepEquals(t, n.Routes, []routers.Route{ + { + DestinationCIDR: "10.0.1.0/24", + NextHop: "10.0.0.11", + }, + { + DestinationCIDR: "10.0.2.0/24", + NextHop: "10.0.0.12", + }, + }) +} diff --git a/openstack/networking/v2/extensions/layer3/extraroutes/urls.go b/openstack/networking/v2/extensions/layer3/extraroutes/urls.go new file mode 100644 index 0000000000..ac91a20c25 --- /dev/null +++ b/openstack/networking/v2/extensions/layer3/extraroutes/urls.go @@ -0,0 +1,13 @@ +package extraroutes + +import "github.com/gophercloud/gophercloud" + +const resourcePath = "routers" + +func addExtraRoutesURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL(resourcePath, id, "add_extraroutes") +} + +func removeExtraRoutesURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL(resourcePath, id, "remove_extraroutes") +} From c26b6e6e737e4bacafa2b6ad2a88531998e1757b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= <m.andre@redhat.com> Date: Fri, 24 Feb 2023 08:47:56 +0100 Subject: [PATCH 204/360] Bump devstack-action Version 0.10 changes the default GIT_BASE to point to github repos rather than opendev. --- .github/workflows/functional-baremetal.yaml | 2 +- .github/workflows/functional-basic.yaml | 2 +- .github/workflows/functional-blockstorage.yaml | 2 +- .github/workflows/functional-clustering.yaml | 2 +- .github/workflows/functional-compute.yaml | 2 +- .github/workflows/functional-containerinfra.yaml | 2 +- .github/workflows/functional-dns.yaml | 2 +- .github/workflows/functional-identity.yaml | 2 +- .github/workflows/functional-imageservice.yaml | 2 +- .github/workflows/functional-keymanager.yaml | 2 +- .github/workflows/functional-loadbalancer.yaml | 2 +- .github/workflows/functional-messaging.yaml | 2 +- .github/workflows/functional-networking.yaml | 2 +- .github/workflows/functional-objectstorage.yaml | 2 +- .github/workflows/functional-orchestration.yaml | 2 +- .github/workflows/functional-placement.yaml | 2 +- .github/workflows/functional-sharedfilesystems.yaml | 2 +- 17 files changed, 17 insertions(+), 17 deletions(-) diff --git a/.github/workflows/functional-baremetal.yaml b/.github/workflows/functional-baremetal.yaml index 90976bcf19..6dd604c4a0 100644 --- a/.github/workflows/functional-baremetal.yaml +++ b/.github/workflows/functional-baremetal.yaml @@ -35,7 +35,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.9 + uses: EmilienM/devstack-action@v0.10 with: branch: ${{ matrix.openstack_version }} conf_overrides: | diff --git a/.github/workflows/functional-basic.yaml b/.github/workflows/functional-basic.yaml index 778f7bcad2..b0f5e49dd8 100644 --- a/.github/workflows/functional-basic.yaml +++ b/.github/workflows/functional-basic.yaml @@ -41,7 +41,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.9 + uses: EmilienM/devstack-action@v0.10 with: branch: ${{ matrix.openstack_version }} enabled_services: 's-account,s-container,s-object,s-proxy' diff --git a/.github/workflows/functional-blockstorage.yaml b/.github/workflows/functional-blockstorage.yaml index 8baed7dc45..fc935cbadd 100644 --- a/.github/workflows/functional-blockstorage.yaml +++ b/.github/workflows/functional-blockstorage.yaml @@ -38,7 +38,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.9 + uses: EmilienM/devstack-action@v0.10 with: branch: ${{ matrix.openstack_version }} conf_overrides: | diff --git a/.github/workflows/functional-clustering.yaml b/.github/workflows/functional-clustering.yaml index 7c1c6bc6a7..493b0e0f23 100644 --- a/.github/workflows/functional-clustering.yaml +++ b/.github/workflows/functional-clustering.yaml @@ -38,7 +38,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.9 + uses: EmilienM/devstack-action@v0.10 with: branch: ${{ matrix.openstack_version }} conf_overrides: | diff --git a/.github/workflows/functional-compute.yaml b/.github/workflows/functional-compute.yaml index f15786a2b0..5e51868d70 100644 --- a/.github/workflows/functional-compute.yaml +++ b/.github/workflows/functional-compute.yaml @@ -38,7 +38,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.9 + uses: EmilienM/devstack-action@v0.10 with: branch: ${{ matrix.openstack_version }} conf_overrides: | diff --git a/.github/workflows/functional-containerinfra.yaml b/.github/workflows/functional-containerinfra.yaml index 98733788f0..3d45315225 100644 --- a/.github/workflows/functional-containerinfra.yaml +++ b/.github/workflows/functional-containerinfra.yaml @@ -52,7 +52,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.9 + uses: EmilienM/devstack-action@v0.10 with: branch: ${{ matrix.openstack_version }} conf_overrides: | diff --git a/.github/workflows/functional-dns.yaml b/.github/workflows/functional-dns.yaml index 5b53df3690..268dd86c37 100644 --- a/.github/workflows/functional-dns.yaml +++ b/.github/workflows/functional-dns.yaml @@ -53,7 +53,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.9 + uses: EmilienM/devstack-action@v0.10 with: branch: ${{ matrix.openstack_version }} conf_overrides: | diff --git a/.github/workflows/functional-identity.yaml b/.github/workflows/functional-identity.yaml index 58b7c08434..2efa905949 100644 --- a/.github/workflows/functional-identity.yaml +++ b/.github/workflows/functional-identity.yaml @@ -38,7 +38,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.9 + uses: EmilienM/devstack-action@v0.10 with: branch: ${{ matrix.openstack_version }} - name: Checkout go diff --git a/.github/workflows/functional-imageservice.yaml b/.github/workflows/functional-imageservice.yaml index 5059267da8..c505ea1212 100644 --- a/.github/workflows/functional-imageservice.yaml +++ b/.github/workflows/functional-imageservice.yaml @@ -38,7 +38,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.9 + uses: EmilienM/devstack-action@v0.10 with: branch: ${{ matrix.openstack_version }} - name: Checkout go diff --git a/.github/workflows/functional-keymanager.yaml b/.github/workflows/functional-keymanager.yaml index 53f0f0e15f..58ef3715d1 100644 --- a/.github/workflows/functional-keymanager.yaml +++ b/.github/workflows/functional-keymanager.yaml @@ -38,7 +38,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.9 + uses: EmilienM/devstack-action@v0.10 with: branch: ${{ matrix.openstack_version }} conf_overrides: | diff --git a/.github/workflows/functional-loadbalancer.yaml b/.github/workflows/functional-loadbalancer.yaml index e73fb303b7..6e492bc3b7 100644 --- a/.github/workflows/functional-loadbalancer.yaml +++ b/.github/workflows/functional-loadbalancer.yaml @@ -38,7 +38,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.9 + uses: EmilienM/devstack-action@v0.10 with: branch: ${{ matrix.openstack_version }} conf_overrides: | diff --git a/.github/workflows/functional-messaging.yaml b/.github/workflows/functional-messaging.yaml index 92dee6f912..06b95b6762 100644 --- a/.github/workflows/functional-messaging.yaml +++ b/.github/workflows/functional-messaging.yaml @@ -38,7 +38,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.9 + uses: EmilienM/devstack-action@v0.10 with: branch: ${{ matrix.openstack_version }} conf_overrides: | diff --git a/.github/workflows/functional-networking.yaml b/.github/workflows/functional-networking.yaml index dd1ccd7ff5..f1f8684dd6 100644 --- a/.github/workflows/functional-networking.yaml +++ b/.github/workflows/functional-networking.yaml @@ -61,7 +61,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.9 + uses: EmilienM/devstack-action@v0.10 with: branch: ${{ matrix.openstack_version }} conf_overrides: | diff --git a/.github/workflows/functional-objectstorage.yaml b/.github/workflows/functional-objectstorage.yaml index f87be4b502..e8d1855670 100644 --- a/.github/workflows/functional-objectstorage.yaml +++ b/.github/workflows/functional-objectstorage.yaml @@ -38,7 +38,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.9 + uses: EmilienM/devstack-action@v0.10 with: branch: ${{ matrix.openstack_version }} conf_overrides: | diff --git a/.github/workflows/functional-orchestration.yaml b/.github/workflows/functional-orchestration.yaml index f3030bf2db..b536e9417b 100644 --- a/.github/workflows/functional-orchestration.yaml +++ b/.github/workflows/functional-orchestration.yaml @@ -38,7 +38,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.9 + uses: EmilienM/devstack-action@v0.10 with: branch: ${{ matrix.openstack_version }} conf_overrides: | diff --git a/.github/workflows/functional-placement.yaml b/.github/workflows/functional-placement.yaml index 5f6ba1fcb7..1e59b583f9 100644 --- a/.github/workflows/functional-placement.yaml +++ b/.github/workflows/functional-placement.yaml @@ -38,7 +38,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.9 + uses: EmilienM/devstack-action@v0.10 with: branch: ${{ matrix.openstack_version }} - name: Checkout go diff --git a/.github/workflows/functional-sharedfilesystems.yaml b/.github/workflows/functional-sharedfilesystems.yaml index 83c9d97519..cfe311072c 100644 --- a/.github/workflows/functional-sharedfilesystems.yaml +++ b/.github/workflows/functional-sharedfilesystems.yaml @@ -38,7 +38,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.9 + uses: EmilienM/devstack-action@v0.10 with: branch: ${{ matrix.openstack_version }} conf_overrides: | From e497a11873eb9791d8b60610e24d16948b4f31e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= <m.andre@redhat.com> Date: Fri, 24 Feb 2023 08:50:33 +0100 Subject: [PATCH 205/360] Prefer github mirrors over opendev repos The jobs are running in the Github infra and we should prefer github repos when possible. --- .github/workflows/functional-baremetal.yaml | 2 +- .github/workflows/functional-clustering.yaml | 4 +-- .../workflows/functional-containerinfra.yaml | 18 +++++------ .github/workflows/functional-dns.yaml | 14 ++++---- .github/workflows/functional-keymanager.yaml | 2 +- .../workflows/functional-loadbalancer.yaml | 4 +-- .github/workflows/functional-messaging.yaml | 2 +- .github/workflows/functional-networking.yaml | 32 +++++++++---------- .../workflows/functional-orchestration.yaml | 2 +- .../functional-sharedfilesystems.yaml | 2 +- 10 files changed, 41 insertions(+), 41 deletions(-) diff --git a/.github/workflows/functional-baremetal.yaml b/.github/workflows/functional-baremetal.yaml index 6dd604c4a0..94ba832f3e 100644 --- a/.github/workflows/functional-baremetal.yaml +++ b/.github/workflows/functional-baremetal.yaml @@ -39,7 +39,7 @@ jobs: with: branch: ${{ matrix.openstack_version }} conf_overrides: | - enable_plugin ironic https://opendev.org/openstack/ironic ${{ matrix.openstack_version }} + enable_plugin ironic https://github.com/openstack/ironic ${{ matrix.openstack_version }} LIBS_FROM_GIT=pyghmi,virtualbmc FORCE_CONFIG_DRIVE=True Q_AGENT=openvswitch diff --git a/.github/workflows/functional-clustering.yaml b/.github/workflows/functional-clustering.yaml index 493b0e0f23..65521f14e0 100644 --- a/.github/workflows/functional-clustering.yaml +++ b/.github/workflows/functional-clustering.yaml @@ -42,8 +42,8 @@ jobs: with: branch: ${{ matrix.openstack_version }} conf_overrides: | - enable_plugin senlin https://opendev.org/openstack/senlin ${{ matrix.openstack_version }} - enable_plugin zaqar https://opendev.org/openstack/zaqar ${{ matrix.openstack_version }} + enable_plugin senlin https://github.com/openstack/senlin ${{ matrix.openstack_version }} + enable_plugin zaqar https://github.com/openstack/zaqar ${{ matrix.openstack_version }} ZAQARCLIENT_BRANCH=${{ matrix.openstack_version }} - name: Checkout go uses: actions/setup-go@v3 diff --git a/.github/workflows/functional-containerinfra.yaml b/.github/workflows/functional-containerinfra.yaml index 3d45315225..d444a9cfd3 100644 --- a/.github/workflows/functional-containerinfra.yaml +++ b/.github/workflows/functional-containerinfra.yaml @@ -15,37 +15,37 @@ jobs: openstack_version: "master" ubuntu_version: "20.04" devstack_conf_overrides: | - enable_plugin magnum https://opendev.org/openstack/magnum master + enable_plugin magnum https://github.com/openstack/magnum master - name: "yoga" openstack_version: "stable/yoga" ubuntu_version: "20.04" devstack_conf_overrides: | - enable_plugin magnum https://opendev.org/openstack/magnum stable/yoga + enable_plugin magnum https://github.com/openstack/magnum stable/yoga - name: "xena" openstack_version: "stable/xena" ubuntu_version: "20.04" devstack_conf_overrides: | - enable_plugin magnum https://opendev.org/openstack/magnum stable/xena + enable_plugin magnum https://github.com/openstack/magnum stable/xena - name: "wallaby" openstack_version: "stable/wallaby" ubuntu_version: "20.04" devstack_conf_overrides: | - enable_plugin magnum https://opendev.org/openstack/magnum stable/wallaby + enable_plugin magnum https://github.com/openstack/magnum stable/wallaby - name: "victoria" openstack_version: "stable/victoria" ubuntu_version: "20.04" devstack_conf_overrides: | - enable_plugin magnum https://opendev.org/openstack/magnum stable/victoria + enable_plugin magnum https://github.com/openstack/magnum stable/victoria - name: "ussuri" openstack_version: "stable/ussuri" ubuntu_version: "18.04" devstack_conf_overrides: | - enable_plugin magnum https://opendev.org/openstack/magnum ussuri-eol + enable_plugin magnum https://github.com/openstack/magnum ussuri-eol - name: "train" openstack_version: "stable/train" ubuntu_version: "18.04" devstack_conf_overrides: | - enable_plugin magnum https://opendev.org/openstack/magnum train-eol + enable_plugin magnum https://github.com/openstack/magnum train-eol runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Deploy OpenStack ${{ matrix.name }} with Magnum and run containerinfra acceptance tests steps: @@ -56,8 +56,8 @@ jobs: with: branch: ${{ matrix.openstack_version }} conf_overrides: | - enable_plugin barbican https://opendev.org/openstack/barbican ${{ matrix.openstack_version }} - enable_plugin heat https://opendev.org/openstack/heat ${{ matrix.openstack_version }} + enable_plugin barbican https://github.com/openstack/barbican ${{ matrix.openstack_version }} + enable_plugin heat https://github.com/openstack/heat ${{ matrix.openstack_version }} GLANCE_LIMIT_IMAGE_SIZE_TOTAL=5000 SWIFT_MAX_FILE_SIZE=5368709122 KEYSTONE_ADMIN_ENDPOINT=true diff --git a/.github/workflows/functional-dns.yaml b/.github/workflows/functional-dns.yaml index 268dd86c37..b7509c6b2e 100644 --- a/.github/workflows/functional-dns.yaml +++ b/.github/workflows/functional-dns.yaml @@ -16,37 +16,37 @@ jobs: openstack_version: "master" ubuntu_version: "20.04" devstack_conf_overrides: | - enable_plugin designate https://opendev.org/openstack/designate master + enable_plugin designate https://github.com/openstack/designate master - name: "yoga" openstack_version: "stable/yoga" ubuntu_version: "20.04" devstack_conf_overrides: | - enable_plugin designate https://opendev.org/openstack/designate stable/yoga + enable_plugin designate https://github.com/openstack/designate stable/yoga - name: "xena" openstack_version: "stable/xena" ubuntu_version: "20.04" devstack_conf_overrides: | - enable_plugin designate https://opendev.org/openstack/designate stable/xena + enable_plugin designate https://github.com/openstack/designate stable/xena - name: "wallaby" openstack_version: "stable/wallaby" ubuntu_version: "20.04" devstack_conf_overrides: | - enable_plugin designate https://opendev.org/openstack/designate stable/wallaby + enable_plugin designate https://github.com/openstack/designate stable/wallaby - name: "victoria" openstack_version: "stable/victoria" ubuntu_version: "20.04" devstack_conf_overrides: | - enable_plugin designate https://opendev.org/openstack/designate stable/victoria + enable_plugin designate https://github.com/openstack/designate stable/victoria - name: "ussuri" openstack_version: "stable/ussuri" ubuntu_version: "18.04" devstack_conf_overrides: | - enable_plugin designate https://opendev.org/openstack/designate stable/ussuri + enable_plugin designate https://github.com/openstack/designate stable/ussuri - name: "train" openstack_version: "stable/train" ubuntu_version: "18.04" devstack_conf_overrides: | - enable_plugin designate https://opendev.org/openstack/designate train-eol + enable_plugin designate https://github.com/openstack/designate train-eol runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Deploy OpenStack ${{ matrix.name }} with Designate and run dns acceptance tests steps: diff --git a/.github/workflows/functional-keymanager.yaml b/.github/workflows/functional-keymanager.yaml index 58ef3715d1..799d4e63e1 100644 --- a/.github/workflows/functional-keymanager.yaml +++ b/.github/workflows/functional-keymanager.yaml @@ -42,7 +42,7 @@ jobs: with: branch: ${{ matrix.openstack_version }} conf_overrides: | - enable_plugin barbican https://opendev.org/openstack/barbican ${{ matrix.openstack_version }} + enable_plugin barbican https://github.com/openstack/barbican ${{ matrix.openstack_version }} enabled_services: 'barbican-svc,barbican-retry,barbican-keystone-listener' - name: Checkout go uses: actions/setup-go@v3 diff --git a/.github/workflows/functional-loadbalancer.yaml b/.github/workflows/functional-loadbalancer.yaml index 6e492bc3b7..58173fa4bd 100644 --- a/.github/workflows/functional-loadbalancer.yaml +++ b/.github/workflows/functional-loadbalancer.yaml @@ -42,8 +42,8 @@ jobs: with: branch: ${{ matrix.openstack_version }} conf_overrides: | - enable_plugin octavia https://opendev.org/openstack/octavia ${{ matrix.openstack_version }} - enable_plugin neutron https://opendev.org/openstack/neutron ${{ matrix.openstack_version }} + enable_plugin octavia https://github.com/openstack/octavia ${{ matrix.openstack_version }} + enable_plugin neutron https://github.com/openstack/neutron ${{ matrix.openstack_version }} enabled_services: 'octavia,o-api,o-cw,o-hk,o-hm,o-da,neutron-qos' - name: Checkout go uses: actions/setup-go@v3 diff --git a/.github/workflows/functional-messaging.yaml b/.github/workflows/functional-messaging.yaml index 06b95b6762..d1b137c784 100644 --- a/.github/workflows/functional-messaging.yaml +++ b/.github/workflows/functional-messaging.yaml @@ -42,7 +42,7 @@ jobs: with: branch: ${{ matrix.openstack_version }} conf_overrides: | - enable_plugin zaqar https://opendev.org/openstack/zaqar ${{ matrix.openstack_version }} + enable_plugin zaqar https://github.com/openstack/zaqar ${{ matrix.openstack_version }} ZAQARCLIENT_BRANCH=${{ matrix.openstack_version }} - name: Checkout go uses: actions/setup-go@v3 diff --git a/.github/workflows/functional-networking.yaml b/.github/workflows/functional-networking.yaml index f1f8684dd6..ba18a010a7 100644 --- a/.github/workflows/functional-networking.yaml +++ b/.github/workflows/functional-networking.yaml @@ -15,46 +15,46 @@ jobs: openstack_version: "master" ubuntu_version: "20.04" devstack_conf_overrides: | - enable_plugin neutron-dynamic-routing https://opendev.org/openstack/neutron-dynamic-routing master - enable_plugin neutron-vpnaas https://opendev.org/openstack/neutron-vpnaas master + enable_plugin neutron-dynamic-routing https://github.com/openstack/neutron-dynamic-routing master + enable_plugin neutron-vpnaas https://github.com/openstack/neutron-vpnaas master - name: "yoga" openstack_version: "stable/yoga" ubuntu_version: "20.04" devstack_conf_overrides: | - enable_plugin neutron-dynamic-routing https://opendev.org/openstack/neutron-dynamic-routing stable/yoga - enable_plugin neutron-vpnaas https://opendev.org/openstack/neutron-vpnaas stable/yoga + enable_plugin neutron-dynamic-routing https://github.com/openstack/neutron-dynamic-routing stable/yoga + enable_plugin neutron-vpnaas https://github.com/openstack/neutron-vpnaas stable/yoga - name: "xena" openstack_version: "stable/xena" ubuntu_version: "20.04" devstack_conf_overrides: | - enable_plugin neutron-dynamic-routing https://opendev.org/openstack/neutron-dynamic-routing stable/xena - enable_plugin neutron-vpnaas https://opendev.org/openstack/neutron-vpnaas stable/xena + enable_plugin neutron-dynamic-routing https://github.com/openstack/neutron-dynamic-routing stable/xena + enable_plugin neutron-vpnaas https://github.com/openstack/neutron-vpnaas stable/xena - name: "wallaby" openstack_version: "stable/wallaby" ubuntu_version: "20.04" devstack_conf_overrides: | - enable_plugin neutron-dynamic-routing https://opendev.org/openstack/neutron-dynamic-routing stable/wallaby - enable_plugin neutron-vpnaas https://opendev.org/openstack/neutron-vpnaas stable/wallaby + enable_plugin neutron-dynamic-routing https://github.com/openstack/neutron-dynamic-routing stable/wallaby + enable_plugin neutron-vpnaas https://github.com/openstack/neutron-vpnaas stable/wallaby - name: "victoria" openstack_version: "stable/victoria" ubuntu_version: "20.04" devstack_conf_overrides: | - enable_plugin neutron-dynamic-routing https://opendev.org/openstack/neutron-dynamic-routing stable/victoria - enable_plugin neutron-vpnaas https://opendev.org/openstack/neutron-vpnaas stable/victoria + enable_plugin neutron-dynamic-routing https://github.com/openstack/neutron-dynamic-routing stable/victoria + enable_plugin neutron-vpnaas https://github.com/openstack/neutron-vpnaas stable/victoria - name: "ussuri" openstack_version: "stable/ussuri" ubuntu_version: "18.04" devstack_conf_overrides: | - enable_plugin neutron-fwaas https://opendev.org/openstack/neutron-fwaas stable/ussuri - enable_plugin neutron-dynamic-routing https://opendev.org/openstack/neutron-dynamic-routing stable/ussuri - enable_plugin neutron-vpnaas https://opendev.org/openstack/neutron-vpnaas ussuri-eol + enable_plugin neutron-fwaas https://github.com/openstack/neutron-fwaas stable/ussuri + enable_plugin neutron-dynamic-routing https://github.com/openstack/neutron-dynamic-routing stable/ussuri + enable_plugin neutron-vpnaas https://github.com/openstack/neutron-vpnaas ussuri-eol - name: "train" openstack_version: "stable/train" ubuntu_version: "18.04" devstack_conf_overrides: | - enable_plugin neutron-fwaas https://opendev.org/openstack/neutron-fwaas stable/train - enable_plugin neutron-dynamic-routing https://opendev.org/openstack/neutron-dynamic-routing train-eol - enable_plugin neutron-vpnaas https://opendev.org/openstack/neutron-vpnaas train-eol + enable_plugin neutron-fwaas https://github.com/openstack/neutron-fwaas stable/train + enable_plugin neutron-dynamic-routing https://github.com/openstack/neutron-dynamic-routing train-eol + enable_plugin neutron-vpnaas https://github.com/openstack/neutron-vpnaas train-eol runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Deploy OpenStack ${{ matrix.name }} with Neutron and run networking acceptance tests steps: diff --git a/.github/workflows/functional-orchestration.yaml b/.github/workflows/functional-orchestration.yaml index b536e9417b..8c0bb984a9 100644 --- a/.github/workflows/functional-orchestration.yaml +++ b/.github/workflows/functional-orchestration.yaml @@ -42,7 +42,7 @@ jobs: with: branch: ${{ matrix.openstack_version }} conf_overrides: | - enable_plugin heat https://opendev.org/openstack/heat ${{ matrix.openstack_version }} + enable_plugin heat https://github.com/openstack/heat ${{ matrix.openstack_version }} enabled_services: 'h-eng,h-api,h-api-cfn,h-api-cw' - name: Checkout go uses: actions/setup-go@v3 diff --git a/.github/workflows/functional-sharedfilesystems.yaml b/.github/workflows/functional-sharedfilesystems.yaml index cfe311072c..9b6178abee 100644 --- a/.github/workflows/functional-sharedfilesystems.yaml +++ b/.github/workflows/functional-sharedfilesystems.yaml @@ -42,7 +42,7 @@ jobs: with: branch: ${{ matrix.openstack_version }} conf_overrides: | - enable_plugin manila https://opendev.org/openstack/manila ${{ matrix.openstack_version }} + enable_plugin manila https://github.com/openstack/manila ${{ matrix.openstack_version }} # LVM Backend config options MANILA_SERVICE_IMAGE_ENABLED=False SHARE_DRIVER=manila.share.drivers.lvm.LVMShareDriver From e7de1a394a6e79573e775cbbe31683cbdb89781f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= <m.andre@redhat.com> Date: Fri, 24 Feb 2023 17:01:40 +0100 Subject: [PATCH 206/360] Baremetal: pull pyghmi from opendev It's not mirrored on github. --- .github/workflows/functional-baremetal.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/functional-baremetal.yaml b/.github/workflows/functional-baremetal.yaml index 94ba832f3e..f9148a4f85 100644 --- a/.github/workflows/functional-baremetal.yaml +++ b/.github/workflows/functional-baremetal.yaml @@ -39,6 +39,8 @@ jobs: with: branch: ${{ matrix.openstack_version }} conf_overrides: | + # pyghmi is not mirrored on github + PYGHMI_REPO=https://opendev.org/x/pyghmi enable_plugin ironic https://github.com/openstack/ironic ${{ matrix.openstack_version }} LIBS_FROM_GIT=pyghmi,virtualbmc FORCE_CONFIG_DRIVE=True From ebf9f9c53d395ab24483ad03d1ff323f4ff9d74f Mon Sep 17 00:00:00 2001 From: kayrus <kay.diam@gmail.com> Date: Wed, 7 Jul 2021 12:18:06 +0200 Subject: [PATCH 207/360] Swift V1: support object versioning --- .../objectstorage/v1/versioning_test.go | 189 ++++++++++++++++++ .../objectstorage/v1/containers/requests.go | 2 + .../objectstorage/v1/containers/results.go | 15 +- .../v1/containers/testing/fixtures.go | 49 +++++ .../v1/containers/testing/requests_test.go | 60 +++++- .../objectstorage/v1/objects/requests.go | 35 +++- openstack/objectstorage/v1/objects/results.go | 42 ++-- .../v1/objects/testing/fixtures.go | 14 ++ .../v1/objects/testing/requests_test.go | 11 + 9 files changed, 387 insertions(+), 30 deletions(-) create mode 100644 acceptance/openstack/objectstorage/v1/versioning_test.go diff --git a/acceptance/openstack/objectstorage/v1/versioning_test.go b/acceptance/openstack/objectstorage/v1/versioning_test.go new file mode 100644 index 0000000000..c0227f346a --- /dev/null +++ b/acceptance/openstack/objectstorage/v1/versioning_test.go @@ -0,0 +1,189 @@ +//go:build acceptance +// +build acceptance + +package v1 + +import ( + "strings" + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/objectstorage/v1/containers" + "github.com/gophercloud/gophercloud/openstack/objectstorage/v1/objects" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestObjectsVersioning(t *testing.T) { + t.Skip("Skip this test, versioning is not yet supported by test env") + + client, err := clients.NewObjectStorageV1Client() + if err != nil { + t.Fatalf("Unable to create client: %v", err) + } + + // Make a slice of length numObjects to hold the random object names. + oNames := make([]string, numObjects) + for i := 0; i < len(oNames); i++ { + oNames[i] = tools.RandomString("test-object-", 8) + } + + // Create a container to hold the test objects. + cName := tools.RandomString("test-container-", 8) + opts := containers.CreateOpts{ + VersionsEnabled: true, + } + header, err := containers.Create(client, cName, opts).Extract() + th.AssertNoErr(t, err) + t.Logf("Create container headers: %+v\n", header) + + // Defer deletion of the container until after testing. + defer func() { + res := containers.Delete(client, cName) + th.AssertNoErr(t, res.Err) + }() + + // ensure versioning is enabled + get, err := containers.Get(client, cName, nil).Extract() + th.AssertNoErr(t, err) + t.Logf("Get container headers: %+v\n", get) + th.AssertEquals(t, true, get.VersionsEnabled) + + // Create a slice of buffers to hold the test object content. + oContents := make([]string, numObjects) + oContentVersionIDs := make([]string, numObjects) + for i := 0; i < numObjects; i++ { + oContents[i] = tools.RandomString("", 10) + createOpts := objects.CreateOpts{ + Content: strings.NewReader(oContents[i]), + } + obj, err := objects.Create(client, cName, oNames[i], createOpts).Extract() + th.AssertNoErr(t, err) + oContentVersionIDs[i] = obj.ObjectVersionID + } + oNewContents := make([]string, numObjects) + for i := 0; i < numObjects; i++ { + oNewContents[i] = tools.RandomString("", 10) + createOpts := objects.CreateOpts{ + Content: strings.NewReader(oNewContents[i]), + } + _, err := objects.Create(client, cName, oNames[i], createOpts).Extract() + th.AssertNoErr(t, err) + } + // Delete the objects after testing two times. + defer func() { + // disable object versioning + opts := containers.UpdateOpts{ + VersionsEnabled: new(bool), + } + header, err := containers.Update(client, cName, opts).Extract() + th.AssertNoErr(t, err) + + t.Logf("Update container headers: %+v\n", header) + + // ensure versioning is disabled + get, err := containers.Get(client, cName, nil).Extract() + th.AssertNoErr(t, err) + t.Logf("Get container headers: %+v\n", get) + th.AssertEquals(t, false, get.VersionsEnabled) + + // delete all object versions before deleting the container + currentVersionIDs := make([]string, numObjects) + for i := 0; i < numObjects; i++ { + opts := objects.DeleteOpts{ + ObjectVersionID: oContentVersionIDs[i], + } + obj, err := objects.Delete(client, cName, oNames[i], opts).Extract() + th.AssertNoErr(t, err) + currentVersionIDs[i] = obj.ObjectCurrentVersionID + } + for i := 0; i < numObjects; i++ { + opts := objects.DeleteOpts{ + ObjectVersionID: currentVersionIDs[i], + } + res := objects.Delete(client, cName, oNames[i], opts) + th.AssertNoErr(t, res.Err) + } + }() + + // List created objects + listOpts := objects.ListOpts{ + Full: true, + Prefix: "test-object-", + } + + allPages, err := objects.List(client, cName, listOpts).AllPages() + if err != nil { + t.Fatalf("Unable to list objects: %v", err) + } + + ons, err := objects.ExtractNames(allPages) + if err != nil { + t.Fatalf("Unable to extract objects: %v", err) + } + th.AssertEquals(t, len(ons), len(oNames)) + + ois, err := objects.ExtractInfo(allPages) + if err != nil { + t.Fatalf("Unable to extract object info: %v", err) + } + th.AssertEquals(t, len(ois), len(oNames)) + + // List all created objects + listOpts = objects.ListOpts{ + Full: true, + Prefix: "test-object-", + Versions: true, + } + + allPages, err = objects.List(client, cName, listOpts).AllPages() + if err != nil { + t.Fatalf("Unable to list objects: %v", err) + } + + ons, err = objects.ExtractNames(allPages) + if err != nil { + t.Fatalf("Unable to extract objects: %v", err) + } + th.AssertEquals(t, len(ons), 2*len(oNames)) + + ois, err = objects.ExtractInfo(allPages) + if err != nil { + t.Fatalf("Unable to extract object info: %v", err) + } + th.AssertEquals(t, len(ois), 2*len(oNames)) + + // ensure proper versioning attributes are set + for i, obj := range ois { + if i%2 == 0 { + th.AssertEquals(t, true, obj.IsLatest) + } else { + th.AssertEquals(t, false, obj.IsLatest) + } + if obj.VersionID == "" { + t.Fatalf("Unexpected empty version_id for the %s object", obj.Name) + } + } + + // Download one of the objects that was created above. + downloadres := objects.Download(client, cName, oNames[0], nil) + th.AssertNoErr(t, downloadres.Err) + + o1Content, err := downloadres.ExtractContent() + th.AssertNoErr(t, err) + + // Compare the two object's contents to test that the copy worked. + th.AssertEquals(t, oNewContents[0], string(o1Content)) + + // Download the another object that was create above. + downloadOpts := objects.DownloadOpts{ + Newest: true, + } + downloadres = objects.Download(client, cName, oNames[1], downloadOpts) + th.AssertNoErr(t, downloadres.Err) + o2Content, err := downloadres.ExtractContent() + th.AssertNoErr(t, err) + + // Compare the two object's contents to test that the copy worked. + th.AssertEquals(t, oNewContents[1], string(o2Content)) +} diff --git a/openstack/objectstorage/v1/containers/requests.go b/openstack/objectstorage/v1/containers/requests.go index dbfae3a061..0957702447 100644 --- a/openstack/objectstorage/v1/containers/requests.go +++ b/openstack/objectstorage/v1/containers/requests.go @@ -80,6 +80,7 @@ type CreateOpts struct { TempURLKey string `h:"X-Container-Meta-Temp-URL-Key"` TempURLKey2 string `h:"X-Container-Meta-Temp-URL-Key-2"` StoragePolicy string `h:"X-Storage-Policy"` + VersionsEnabled bool `h:"X-Versions-Enabled"` } // ToContainerCreateMap formats a CreateOpts into a map of headers. @@ -176,6 +177,7 @@ type UpdateOpts struct { HistoryLocation string `h:"X-History-Location"` TempURLKey string `h:"X-Container-Meta-Temp-URL-Key"` TempURLKey2 string `h:"X-Container-Meta-Temp-URL-Key-2"` + VersionsEnabled *bool `h:"X-Versions-Enabled"` } // ToContainerUpdateMap formats a UpdateOpts into a map of headers. diff --git a/openstack/objectstorage/v1/containers/results.go b/openstack/objectstorage/v1/containers/results.go index c6dc61fa9f..73de4c0135 100644 --- a/openstack/objectstorage/v1/containers/results.go +++ b/openstack/objectstorage/v1/containers/results.go @@ -3,6 +3,7 @@ package containers import ( "encoding/json" "fmt" + "strconv" "strings" "time" @@ -109,15 +110,17 @@ type GetHeader struct { TempURLKey string `json:"X-Container-Meta-Temp-URL-Key"` TempURLKey2 string `json:"X-Container-Meta-Temp-URL-Key-2"` Timestamp float64 `json:"X-Timestamp,string"` + VersionsEnabled bool `json:"-"` } func (r *GetHeader) UnmarshalJSON(b []byte) error { type tmp GetHeader var s struct { tmp - Write string `json:"X-Container-Write"` - Read string `json:"X-Container-Read"` - Date gophercloud.JSONRFC1123 `json:"Date"` + Write string `json:"X-Container-Write"` + Read string `json:"X-Container-Read"` + Date gophercloud.JSONRFC1123 `json:"Date"` + VersionsEnabled string `json:"X-Versions-Enabled"` } err := json.Unmarshal(b, &s) @@ -132,6 +135,12 @@ func (r *GetHeader) UnmarshalJSON(b []byte) error { r.Date = time.Time(s.Date) + if s.VersionsEnabled != "" { + // custom unmarshaller here is required to handle boolean value + // that starts with a capital letter + r.VersionsEnabled, err = strconv.ParseBool(s.VersionsEnabled) + } + return err } diff --git a/openstack/objectstorage/v1/containers/testing/fixtures.go b/openstack/objectstorage/v1/containers/testing/fixtures.go index 1edc147bb8..aa38b68da7 100644 --- a/openstack/objectstorage/v1/containers/testing/fixtures.go +++ b/openstack/objectstorage/v1/containers/testing/fixtures.go @@ -207,6 +207,54 @@ func HandleUpdateContainerSuccessfully(t *testing.T, options ...option) { }) } +// HandleUpdateContainerVersioningOn creates an HTTP handler at `/testVersioning` on the test handler mux that +// responds with a `Update` response. +func HandleUpdateContainerVersioningOn(t *testing.T, options ...option) { + ho := handlerOptions{ + path: "/testVersioning", + } + for _, apply := range options { + apply(&ho) + } + + th.Mux.HandleFunc(ho.path, func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestHeader(t, r, "Accept", "application/json") + th.TestHeader(t, r, "X-Container-Write", "") + th.TestHeader(t, r, "X-Container-Read", "") + th.TestHeader(t, r, "X-Container-Sync-To", "") + th.TestHeader(t, r, "X-Container-Sync-Key", "") + th.TestHeader(t, r, "Content-Type", "text/plain") + th.TestHeader(t, r, "X-Versions-Enabled", "true") + w.WriteHeader(http.StatusNoContent) + }) +} + +// HandleUpdateContainerVersioningOff creates an HTTP handler at `/testVersioning` on the test handler mux that +// responds with a `Update` response. +func HandleUpdateContainerVersioningOff(t *testing.T, options ...option) { + ho := handlerOptions{ + path: "/testVersioning", + } + for _, apply := range options { + apply(&ho) + } + + th.Mux.HandleFunc(ho.path, func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestHeader(t, r, "Accept", "application/json") + th.TestHeader(t, r, "X-Container-Write", "") + th.TestHeader(t, r, "X-Container-Read", "") + th.TestHeader(t, r, "X-Container-Sync-To", "") + th.TestHeader(t, r, "X-Container-Sync-Key", "") + th.TestHeader(t, r, "Content-Type", "text/plain") + th.TestHeader(t, r, "X-Versions-Enabled", "false") + w.WriteHeader(http.StatusNoContent) + }) +} + // HandleGetContainerSuccessfully creates an HTTP handler at `/testContainer` on the test handler mux that // responds with a `Get` response. func HandleGetContainerSuccessfully(t *testing.T, options ...option) { @@ -231,6 +279,7 @@ func HandleGetContainerSuccessfully(t *testing.T, options ...option) { w.Header().Set("X-Timestamp", "1471298837.95721") w.Header().Set("X-Trans-Id", "tx554ed59667a64c61866f1-0057b4ba37") w.Header().Set("X-Storage-Policy", "test_policy") + w.Header().Set("X-Versions-Enabled", "True") w.WriteHeader(http.StatusNoContent) }) } diff --git a/openstack/objectstorage/v1/containers/testing/requests_test.go b/openstack/objectstorage/v1/containers/testing/requests_test.go index 91cca156cc..cc40e2151e 100644 --- a/openstack/objectstorage/v1/containers/testing/requests_test.go +++ b/openstack/objectstorage/v1/containers/testing/requests_test.go @@ -236,18 +236,58 @@ func TestGetContainer(t *testing.T) { th.AssertNoErr(t, err) expected := &containers.GetHeader{ - AcceptRanges: "bytes", - BytesUsed: 100, - ContentType: "application/json; charset=utf-8", - Date: time.Date(2016, time.August, 17, 19, 25, 43, 0, time.UTC), - ObjectCount: 4, - Read: []string{"test"}, - TransID: "tx554ed59667a64c61866f1-0057b4ba37", - Write: []string{"test2", "user4"}, - StoragePolicy: "test_policy", - Timestamp: 1471298837.95721, + AcceptRanges: "bytes", + BytesUsed: 100, + ContentType: "application/json; charset=utf-8", + Date: time.Date(2016, time.August, 17, 19, 25, 43, 0, time.UTC), + ObjectCount: 4, + Read: []string{"test"}, + TransID: "tx554ed59667a64c61866f1-0057b4ba37", + Write: []string{"test2", "user4"}, + StoragePolicy: "test_policy", + Timestamp: 1471298837.95721, + VersionsEnabled: true, } actual, err := res.Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, expected, actual) } + +func TestUpdateContainerVersioningOff(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleUpdateContainerVersioningOff(t) + + contentType := "text/plain" + options := &containers.UpdateOpts{ + Metadata: map[string]string{"foo": "bar"}, + ContainerWrite: new(string), + ContainerRead: new(string), + ContainerSyncTo: new(string), + ContainerSyncKey: new(string), + ContentType: &contentType, + VersionsEnabled: new(bool), + } + _, err := containers.Update(fake.ServiceClient(), "testVersioning", options).Extract() + th.AssertNoErr(t, err) +} + +func TestUpdateContainerVersioningOn(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleUpdateContainerVersioningOn(t) + + iTrue := true + contentType := "text/plain" + options := &containers.UpdateOpts{ + Metadata: map[string]string{"foo": "bar"}, + ContainerWrite: new(string), + ContainerRead: new(string), + ContainerSyncTo: new(string), + ContainerSyncKey: new(string), + ContentType: &contentType, + VersionsEnabled: &iTrue, + } + _, err := containers.Update(fake.ServiceClient(), "testVersioning", options).Extract() + th.AssertNoErr(t, err) +} diff --git a/openstack/objectstorage/v1/objects/requests.go b/openstack/objectstorage/v1/objects/requests.go index 639dfed472..1a1147a14d 100644 --- a/openstack/objectstorage/v1/objects/requests.go +++ b/openstack/objectstorage/v1/objects/requests.go @@ -37,6 +37,7 @@ type ListOpts struct { Prefix string `q:"prefix"` Delimiter string `q:"delimiter"` Path string `q:"path"` + Versions bool `q:"versions"` } // ToObjectListParams formats a ListOpts into a query string and boolean @@ -95,6 +96,7 @@ type DownloadOpts struct { Expires string `q:"expires"` MultipartManifest string `q:"multipart-manifest"` Signature string `q:"signature"` + ObjectVersionID string `q:"version-id"` } // ToObjectDownloadParams formats a DownloadOpts into a query string and map of @@ -265,6 +267,12 @@ type CopyOptsBuilder interface { ToObjectCopyMap() (map[string]string, error) } +// CopyOptsQueryBuilder allows extensions to add additional query parameters to +// the Copy request. +type CopyOptsQueryBuilder interface { + ToObjectCopyQuery() (string, error) +} + // CopyOpts is a structure that holds parameters for copying one object to // another. type CopyOpts struct { @@ -273,6 +281,7 @@ type CopyOpts struct { ContentEncoding string `h:"Content-Encoding"` ContentType string `h:"Content-Type"` Destination string `h:"Destination" required:"true"` + ObjectVersionID string `q:"version-id"` } // ToObjectCopyMap formats a CopyOpts into a map of headers. @@ -287,6 +296,15 @@ func (opts CopyOpts) ToObjectCopyMap() (map[string]string, error) { return h, nil } +// ToObjectCopyQuery formats a CopyOpts into a query. +func (opts CopyOpts) ToObjectCopyQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + if err != nil { + return "", err + } + return q.String(), nil +} + // Copy is a function that copies one object to another. func Copy(c *gophercloud.ServiceClient, containerName, objectName string, opts CopyOptsBuilder) (r CopyResult) { url, err := copyURL(c, containerName, objectName) @@ -305,6 +323,15 @@ func Copy(c *gophercloud.ServiceClient, containerName, objectName string, opts C h[k] = v } + if opts, ok := opts.(CopyOptsQueryBuilder); ok { + query, err := opts.ToObjectCopyQuery() + if err != nil { + r.Err = err + return + } + url += query + } + resp, err := c.Request("COPY", url, &gophercloud.RequestOpts{ MoreHeaders: h, OkCodes: []int{201}, @@ -322,6 +349,7 @@ type DeleteOptsBuilder interface { // DeleteOpts is a structure that holds parameters for deleting an object. type DeleteOpts struct { MultipartManifest string `q:"multipart-manifest"` + ObjectVersionID string `q:"version-id"` } // ToObjectDeleteQuery formats a DeleteOpts into a query string. @@ -359,9 +387,10 @@ type GetOptsBuilder interface { // GetOpts is a structure that holds parameters for getting an object's // metadata. type GetOpts struct { - Newest bool `h:"X-Newest"` - Expires string `q:"expires"` - Signature string `q:"signature"` + Newest bool `h:"X-Newest"` + Expires string `q:"expires"` + Signature string `q:"signature"` + ObjectVersionID string `q:"version-id"` } // ToObjectGetParams formats a GetOpts into a query string and a map of headers. diff --git a/openstack/objectstorage/v1/objects/results.go b/openstack/objectstorage/v1/objects/results.go index 0afc7e7bf9..6d8d5d304c 100644 --- a/openstack/objectstorage/v1/objects/results.go +++ b/openstack/objectstorage/v1/objects/results.go @@ -32,6 +32,13 @@ type Object struct { // Subdir denotes if the result contains a subdir. Subdir string `json:"subdir"` + + // IsLatest indicates whether the object version is the latest one. + IsLatest bool `json:"is_latest"` + + // VersionID contains a version ID of the object, when container + // versioning is enabled. + VersionID string `json:"version_id"` } func (r *Object) UnmarshalJSON(b []byte) error { @@ -146,6 +153,7 @@ type DownloadHeader struct { ObjectManifest string `json:"X-Object-Manifest"` StaticLargeObject bool `json:"-"` TransID string `json:"X-Trans-Id"` + ObjectVersionID string `json:"X-Object-Version-Id"` } func (r *DownloadHeader) UnmarshalJSON(b []byte) error { @@ -224,6 +232,7 @@ type GetHeader struct { ObjectManifest string `json:"X-Object-Manifest"` StaticLargeObject bool `json:"-"` TransID string `json:"X-Trans-Id"` + ObjectVersionID string `json:"X-Object-Version-Id"` } func (r *GetHeader) UnmarshalJSON(b []byte) error { @@ -290,12 +299,13 @@ func (r GetResult) ExtractMetadata() (map[string]string, error) { // CreateHeader represents the headers returned in the response from a // Create request. type CreateHeader struct { - ContentLength int64 `json:"Content-Length,string"` - ContentType string `json:"Content-Type"` - Date time.Time `json:"-"` - ETag string `json:"Etag"` - LastModified time.Time `json:"-"` - TransID string `json:"X-Trans-Id"` + ContentLength int64 `json:"Content-Length,string"` + ContentType string `json:"Content-Type"` + Date time.Time `json:"-"` + ETag string `json:"Etag"` + LastModified time.Time `json:"-"` + TransID string `json:"X-Trans-Id"` + ObjectVersionID string `json:"X-Object-Version-Id"` } func (r *CreateHeader) UnmarshalJSON(b []byte) error { @@ -337,10 +347,11 @@ func (r CreateResult) Extract() (*CreateHeader, error) { // UpdateHeader represents the headers returned in the response from a // Update request. type UpdateHeader struct { - ContentLength int64 `json:"Content-Length,string"` - ContentType string `json:"Content-Type"` - Date time.Time `json:"-"` - TransID string `json:"X-Trans-Id"` + ContentLength int64 `json:"Content-Length,string"` + ContentType string `json:"Content-Type"` + Date time.Time `json:"-"` + TransID string `json:"X-Trans-Id"` + ObjectVersionID string `json:"X-Object-Version-Id"` } func (r *UpdateHeader) UnmarshalJSON(b []byte) error { @@ -376,10 +387,12 @@ func (r UpdateResult) Extract() (*UpdateHeader, error) { // DeleteHeader represents the headers returned in the response from a // Delete request. type DeleteHeader struct { - ContentLength int64 `json:"Content-Length,string"` - ContentType string `json:"Content-Type"` - Date time.Time `json:"-"` - TransID string `json:"X-Trans-Id"` + ContentLength int64 `json:"Content-Length,string"` + ContentType string `json:"Content-Type"` + Date time.Time `json:"-"` + TransID string `json:"X-Trans-Id"` + ObjectVersionID string `json:"X-Object-Version-Id"` + ObjectCurrentVersionID string `json:"X-Object-Current-Version-Id"` } func (r *DeleteHeader) UnmarshalJSON(b []byte) error { @@ -423,6 +436,7 @@ type CopyHeader struct { ETag string `json:"Etag"` LastModified time.Time `json:"-"` TransID string `json:"X-Trans-Id"` + ObjectVersionID string `json:"X-Object-Version-Id"` } func (r *CopyHeader) UnmarshalJSON(b []byte) error { diff --git a/openstack/objectstorage/v1/objects/testing/fixtures.go b/openstack/objectstorage/v1/objects/testing/fixtures.go index 3dbbe3706d..d21dc74b6b 100644 --- a/openstack/objectstorage/v1/objects/testing/fixtures.go +++ b/openstack/objectstorage/v1/objects/testing/fixtures.go @@ -281,6 +281,20 @@ func HandleCopyObjectSuccessfully(t *testing.T) { }) } +// HandleCopyObjectSuccessfully creates an HTTP handler at `/testContainer/testObject` on the test handler mux that +// responds with a `Copy` response. +func HandleCopyObjectVersionSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/testContainer/testObject", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "COPY") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestHeader(t, r, "Accept", "application/json") + th.TestHeader(t, r, "Destination", "/newTestContainer/newTestObject") + th.TestFormValues(t, r, map[string]string{"version-id": "123456788"}) + w.Header().Set("X-Object-Version-Id", "123456789") + w.WriteHeader(http.StatusCreated) + }) +} + // HandleDeleteObjectSuccessfully creates an HTTP handler at `/testContainer/testObject` on the test handler mux that // responds with a `Delete` response. func HandleDeleteObjectSuccessfully(t *testing.T, options ...option) { diff --git a/openstack/objectstorage/v1/objects/testing/requests_test.go b/openstack/objectstorage/v1/objects/testing/requests_test.go index ac89c8d244..fdac08c211 100644 --- a/openstack/objectstorage/v1/objects/testing/requests_test.go +++ b/openstack/objectstorage/v1/objects/testing/requests_test.go @@ -351,6 +351,17 @@ func TestCopyObject(t *testing.T) { th.AssertNoErr(t, res.Err) } +func TestCopyObjectVersion(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleCopyObjectVersionSuccessfully(t) + + options := &objects.CopyOpts{Destination: "/newTestContainer/newTestObject", ObjectVersionID: "123456788"} + res, err := objects.Copy(fake.ServiceClient(), "testContainer", "testObject", options).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, "123456789", res.ObjectVersionID) +} + func TestDeleteObject(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() From 3c75ef9b09e029d97ee89dbd23ed212b515ee540 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= <m.andre@redhat.com> Date: Tue, 28 Feb 2023 15:37:05 +0100 Subject: [PATCH 208/360] Collect more logs in CI In order to help debugging, it can be useful to have the devstack's local.conf file around as well as an overview of devstack running services. --- script/collectlogs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/script/collectlogs b/script/collectlogs index 8222d54d7e..c8e438d391 100755 --- a/script/collectlogs +++ b/script/collectlogs @@ -4,8 +4,10 @@ set -x LOG_DIR=${LOG_DIR:-/tmp/devstack-logs} mkdir -p $LOG_DIR sudo journalctl -o short-precise --no-pager &> $LOG_DIR/journal.log +sudo systemctl status "devstack@*" &> $LOG_DIR/devstack-services.txt free -m > $LOG_DIR/free.txt dpkg -l > $LOG_DIR/dpkg-l.txt pip freeze > $LOG_DIR/pip-freeze.txt +cp ./devstack/local.conf $LOG_DIR sudo find $LOG_DIR -type d -exec chmod 0755 {} \; sudo find $LOG_DIR -type f -exec chmod 0644 {} \; From 4df330e1ed23790bf02c82e5b7aef096aa9ca211 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= <m.andre@redhat.com> Date: Tue, 28 Feb 2023 18:20:27 +0100 Subject: [PATCH 209/360] Configure devstack for swift object versioning The devstack environment was not setup properly to enable acceptance tests developed for https://github.com/gophercloud/gophercloud/pull/2571. --- .github/workflows/functional-objectstorage.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/functional-objectstorage.yaml b/.github/workflows/functional-objectstorage.yaml index e8d1855670..3a52e4724f 100644 --- a/.github/workflows/functional-objectstorage.yaml +++ b/.github/workflows/functional-objectstorage.yaml @@ -44,6 +44,9 @@ jobs: conf_overrides: | SWIFT_ENABLE_TEMPURLS=True SWIFT_TEMPURL_KEY=secretkey + [[post-config|\$SWIFT_CONFIG_PROXY_SERVER]] + [filter:versioned_writes] + allow_object_versioning = true enabled_services: 's-account,s-container,s-object,s-proxy' - name: Checkout go uses: actions/setup-go@v3 From f0a4052f6597f3b3365b785ec4a3efd990cb5042 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= <m.andre@redhat.com> Date: Tue, 28 Feb 2023 18:21:46 +0100 Subject: [PATCH 210/360] Enable TestObjectsVersioning test above Train The new object versioning mode was introduced in Ussuri [1]. [1] https://docs.openstack.org/releasenotes/swift/ussuri.html#relnotes-2-24-0-stable-ussuri-new-features --- acceptance/openstack/objectstorage/v1/versioning_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acceptance/openstack/objectstorage/v1/versioning_test.go b/acceptance/openstack/objectstorage/v1/versioning_test.go index c0227f346a..30ebadecc6 100644 --- a/acceptance/openstack/objectstorage/v1/versioning_test.go +++ b/acceptance/openstack/objectstorage/v1/versioning_test.go @@ -15,7 +15,7 @@ import ( ) func TestObjectsVersioning(t *testing.T) { - t.Skip("Skip this test, versioning is not yet supported by test env") + clients.SkipReleasesBelow(t, "stable/ussuri") client, err := clients.NewObjectStorageV1Client() if err != nil { From 57f44020e85bad1c4905b97222f45c0feaa1c423 Mon Sep 17 00:00:00 2001 From: emilmaruszczak <98890096+emilmaruszczak@users.noreply.github.com> Date: Wed, 1 Mar 2023 12:07:34 +0100 Subject: [PATCH 211/360] keystone: add v3 OS-FEDERATION mappings update operation (#2550) Add Update operation --- .../openstack/identity/v3/federation_test.go | 36 ++++++ .../identity/v3/extensions/federation/doc.go | 37 ++++++ .../v3/extensions/federation/requests.go | 31 +++++ .../v3/extensions/federation/results.go | 6 + .../extensions/federation/testing/fixtures.go | 122 ++++++++++++++++++ .../federation/testing/requests_test.go | 41 ++++++ 6 files changed, 273 insertions(+) diff --git a/acceptance/openstack/identity/v3/federation_test.go b/acceptance/openstack/identity/v3/federation_test.go index f1fe0a63d6..647d71389e 100644 --- a/acceptance/openstack/identity/v3/federation_test.go +++ b/acceptance/openstack/identity/v3/federation_test.go @@ -73,4 +73,40 @@ func TestMappingsCRUD(t *testing.T) { th.AssertNoErr(t, err) th.AssertEquals(t, len(createOpts.Rules), len(mapping.Rules)) th.CheckDeepEquals(t, createOpts.Rules[0], mapping.Rules[0]) + + updateOpts := federation.UpdateMappingOpts{ + Rules: []federation.MappingRule{ + { + Local: []federation.RuleLocal{ + { + User: &federation.RuleUser{ + Name: "{0}", + }, + }, + { + Group: &federation.Group{ + ID: "0cd5e9", + }, + }, + }, + Remote: []federation.RuleRemote{ + { + Type: "UserName", + }, + { + Type: "orgPersonType", + AnyOneOf: []string{ + "Contractor", + "SubContractor", + }, + }, + }, + }, + }, + } + + updatedMapping, err := federation.UpdateMapping(client, mappingName, updateOpts).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, len(updateOpts.Rules), len(updatedMapping.Rules)) + th.CheckDeepEquals(t, updateOpts.Rules[0], updatedMapping.Rules[0]) } diff --git a/openstack/identity/v3/extensions/federation/doc.go b/openstack/identity/v3/extensions/federation/doc.go index 3c1304cb7a..e3d7d743bb 100644 --- a/openstack/identity/v3/extensions/federation/doc.go +++ b/openstack/identity/v3/extensions/federation/doc.go @@ -57,5 +57,42 @@ Example to Get a Mapping if err != nil { panic(err) } + +Example to Update a Mapping + + updateOpts := federation.UpdateMappingOpts{ + Rules: []federation.MappingRule{ + { + Local: []federation.RuleLocal{ + { + User: &federation.RuleUser{ + Name: "{0}", + }, + }, + { + Group: &federation.Group{ + ID: "0cd5e9", + }, + }, + }, + Remote: []federation.RuleRemote{ + { + Type: "UserName", + }, + { + Type: "orgPersonType", + AnyOneOf: []string{ + "Contractor", + "SubContractor", + }, + }, + }, + }, + }, + } + updatedMapping, err := federation.UpdateMapping(identityClient, "ACME", updateOpts).Extract() + if err != nil { + panic(err) + } */ package federation diff --git a/openstack/identity/v3/extensions/federation/requests.go b/openstack/identity/v3/extensions/federation/requests.go index 2c2e999b1a..b4c729a314 100644 --- a/openstack/identity/v3/extensions/federation/requests.go +++ b/openstack/identity/v3/extensions/federation/requests.go @@ -49,3 +49,34 @@ func GetMapping(client *gophercloud.ServiceClient, mappingID string) (r GetMappi _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } + +// UpdateMappingOptsBuilder allows extensions to add additional parameters to +// the Update request. +type UpdateMappingOptsBuilder interface { + ToMappingUpdateMap() (map[string]interface{}, error) +} + +// UpdateMappingOpts provides options for updating a mapping. +type UpdateMappingOpts struct { + // The list of rules used to map remote users into local users + Rules []MappingRule `json:"rules"` +} + +// ToMappingUpdateMap formats a UpdateOpts into an update request. +func (opts UpdateMappingOpts) ToMappingUpdateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "mapping") +} + +// UpdateMapping updates an existing mapping. +func UpdateMapping(client *gophercloud.ServiceClient, mappingID string, opts UpdateMappingOptsBuilder) (r UpdateMappingResult) { + b, err := opts.ToMappingUpdateMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Patch(mappingsResourceURL(client, mappingID), &b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} diff --git a/openstack/identity/v3/extensions/federation/results.go b/openstack/identity/v3/extensions/federation/results.go index a3262cae90..39dcbe1db2 100644 --- a/openstack/identity/v3/extensions/federation/results.go +++ b/openstack/identity/v3/extensions/federation/results.go @@ -156,6 +156,12 @@ type GetMappingResult struct { mappingResult } +// UpdateMappingResult is the response from a UpdateMapping operation. +// Call its Extract method to interpret it as a Mapping. +type UpdateMappingResult struct { + mappingResult +} + // MappingsPage is a single page of Mapping results. type MappingsPage struct { pagination.LinkedPageBase diff --git a/openstack/identity/v3/extensions/federation/testing/fixtures.go b/openstack/identity/v3/extensions/federation/testing/fixtures.go index 5db051fe89..3da5fc25bb 100644 --- a/openstack/identity/v3/extensions/federation/testing/fixtures.go +++ b/openstack/identity/v3/extensions/federation/testing/fixtures.go @@ -132,6 +132,80 @@ const CreateOutput = ` const GetOutput = CreateOutput +const UpdateRequest = ` +{ + "mapping": { + "rules": [ + { + "local": [ + { + "user": { + "name": "{0}" + } + }, + { + "group": { + "id": "0cd5e9" + } + } + ], + "remote": [ + { + "type": "UserName" + }, + { + "type": "orgPersonType", + "any_one_of": [ + "Contractor", + "SubContractor" + ] + } + ] + } + ] + } +} +` + +const UpdateOutput = ` +{ + "mapping": { + "id": "ACME", + "links": { + "self": "http://example.com/identity/v3/OS-FEDERATION/mappings/ACME" + }, + "rules": [ + { + "local": [ + { + "user": { + "name": "{0}" + } + }, + { + "group": { + "id": "0cd5e9" + } + } + ], + "remote": [ + { + "type": "UserName" + }, + { + "type": "orgPersonType", + "any_one_of": [ + "Contractor", + "SubContractor" + ] + } + ] + } + ] + } +} +` + var MappingACME = federation.Mapping{ ID: "ACME", Links: map[string]interface{}{ @@ -167,6 +241,41 @@ var MappingACME = federation.Mapping{ }, } +var MappingUpdated = federation.Mapping{ + ID: "ACME", + Links: map[string]interface{}{ + "self": "http://example.com/identity/v3/OS-FEDERATION/mappings/ACME", + }, + Rules: []federation.MappingRule{ + { + Local: []federation.RuleLocal{ + { + User: &federation.RuleUser{ + Name: "{0}", + }, + }, + { + Group: &federation.Group{ + ID: "0cd5e9", + }, + }, + }, + Remote: []federation.RuleRemote{ + { + Type: "UserName", + }, + { + Type: "orgPersonType", + AnyOneOf: []string{ + "Contractor", + "SubContractor", + }, + }, + }, + }, + }, +} + // ExpectedMappingsSlice is the slice of mappings expected to be returned from ListOutput. var ExpectedMappingsSlice = []federation.Mapping{MappingACME} @@ -210,3 +319,16 @@ func HandleGetMappingSuccessfully(t *testing.T) { fmt.Fprintf(w, GetOutput) }) } + +// HandleUpdateMappingSuccessfully creates an HTTP handler at `/mappings` on the +// test handler mux that tests mapping update. +func HandleUpdateMappingSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/OS-FEDERATION/mappings/ACME", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PATCH") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + th.TestJSONRequest(t, r, UpdateRequest) + + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, UpdateOutput) + }) +} diff --git a/openstack/identity/v3/extensions/federation/testing/requests_test.go b/openstack/identity/v3/extensions/federation/testing/requests_test.go index 9ac0f5c4c6..f9197d7e98 100644 --- a/openstack/identity/v3/extensions/federation/testing/requests_test.go +++ b/openstack/identity/v3/extensions/federation/testing/requests_test.go @@ -91,3 +91,44 @@ func TestGetMapping(t *testing.T) { th.AssertNoErr(t, err) th.CheckDeepEquals(t, MappingACME, *actual) } + +func TestUpdateMapping(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleUpdateMappingSuccessfully(t) + + updateOpts := federation.UpdateMappingOpts{ + Rules: []federation.MappingRule{ + { + Local: []federation.RuleLocal{ + { + User: &federation.RuleUser{ + Name: "{0}", + }, + }, + { + Group: &federation.Group{ + ID: "0cd5e9", + }, + }, + }, + Remote: []federation.RuleRemote{ + { + Type: "UserName", + }, + { + Type: "orgPersonType", + AnyOneOf: []string{ + "Contractor", + "SubContractor", + }, + }, + }, + }, + }, + } + + actual, err := federation.UpdateMapping(client.ServiceClient(), "ACME", updateOpts).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, MappingUpdated, *actual) +} From 7edb0d08b18f7ea2ca39b0d3fd1a86bf9fee5faa Mon Sep 17 00:00:00 2001 From: emilmaruszczak <98890096+emilmaruszczak@users.noreply.github.com> Date: Thu, 2 Mar 2023 07:57:45 +0100 Subject: [PATCH 212/360] keystone: add v3 OS-FEDERATION mappings delete operation (#2576) Add delete operation --- acceptance/openstack/identity/v3/federation_test.go | 9 +++++++++ openstack/identity/v3/extensions/federation/doc.go | 7 +++++++ .../identity/v3/extensions/federation/requests.go | 7 +++++++ .../identity/v3/extensions/federation/results.go | 6 ++++++ .../v3/extensions/federation/testing/fixtures.go | 11 +++++++++++ .../v3/extensions/federation/testing/requests_test.go | 9 +++++++++ 6 files changed, 49 insertions(+) diff --git a/acceptance/openstack/identity/v3/federation_test.go b/acceptance/openstack/identity/v3/federation_test.go index 647d71389e..a1b286b9f0 100644 --- a/acceptance/openstack/identity/v3/federation_test.go +++ b/acceptance/openstack/identity/v3/federation_test.go @@ -6,6 +6,7 @@ package v3 import ( "testing" + "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/federation" @@ -109,4 +110,12 @@ func TestMappingsCRUD(t *testing.T) { th.AssertNoErr(t, err) th.AssertEquals(t, len(updateOpts.Rules), len(updatedMapping.Rules)) th.CheckDeepEquals(t, updateOpts.Rules[0], updatedMapping.Rules[0]) + + err = federation.DeleteMapping(client, mappingName).ExtractErr() + th.AssertNoErr(t, err) + + resp := federation.GetMapping(client, mappingName) + th.AssertErr(t, resp.Err) + _, ok := resp.Err.(gophercloud.ErrDefault404) + th.AssertEquals(t, true, ok) } diff --git a/openstack/identity/v3/extensions/federation/doc.go b/openstack/identity/v3/extensions/federation/doc.go index e3d7d743bb..8d394d890a 100644 --- a/openstack/identity/v3/extensions/federation/doc.go +++ b/openstack/identity/v3/extensions/federation/doc.go @@ -94,5 +94,12 @@ Example to Update a Mapping if err != nil { panic(err) } + +Example to Delete a Mapping + + err := federation.DeleteMapping(identityClient, "ACME").ExtractErr() + if err != nil { + panic(err) + } */ package federation diff --git a/openstack/identity/v3/extensions/federation/requests.go b/openstack/identity/v3/extensions/federation/requests.go index b4c729a314..7563164975 100644 --- a/openstack/identity/v3/extensions/federation/requests.go +++ b/openstack/identity/v3/extensions/federation/requests.go @@ -80,3 +80,10 @@ func UpdateMapping(client *gophercloud.ServiceClient, mappingID string, opts Upd _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } + +// DeleteMapping deletes a mapping. +func DeleteMapping(client *gophercloud.ServiceClient, mappingID string) (r DeleteMappingResult) { + resp, err := client.Delete(mappingsResourceURL(client, mappingID), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} diff --git a/openstack/identity/v3/extensions/federation/results.go b/openstack/identity/v3/extensions/federation/results.go index 39dcbe1db2..eeb516ced6 100644 --- a/openstack/identity/v3/extensions/federation/results.go +++ b/openstack/identity/v3/extensions/federation/results.go @@ -162,6 +162,12 @@ type UpdateMappingResult struct { mappingResult } +// DeleteMappingResult is the response from a DeleteMapping operation. +// Call its ExtractErr to determine if the request succeeded or failed. +type DeleteMappingResult struct { + gophercloud.ErrResult +} + // MappingsPage is a single page of Mapping results. type MappingsPage struct { pagination.LinkedPageBase diff --git a/openstack/identity/v3/extensions/federation/testing/fixtures.go b/openstack/identity/v3/extensions/federation/testing/fixtures.go index 3da5fc25bb..41b85ce56b 100644 --- a/openstack/identity/v3/extensions/federation/testing/fixtures.go +++ b/openstack/identity/v3/extensions/federation/testing/fixtures.go @@ -332,3 +332,14 @@ func HandleUpdateMappingSuccessfully(t *testing.T) { fmt.Fprintf(w, UpdateOutput) }) } + +// HandleDeleteMappingSuccessfully creates an HTTP handler at `/mappings` on the +// test handler mux that tests mapping deletion. +func HandleDeleteMappingSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/OS-FEDERATION/mappings/ACME", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "DELETE") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + w.WriteHeader(http.StatusNoContent) + }) +} diff --git a/openstack/identity/v3/extensions/federation/testing/requests_test.go b/openstack/identity/v3/extensions/federation/testing/requests_test.go index f9197d7e98..0b55877673 100644 --- a/openstack/identity/v3/extensions/federation/testing/requests_test.go +++ b/openstack/identity/v3/extensions/federation/testing/requests_test.go @@ -132,3 +132,12 @@ func TestUpdateMapping(t *testing.T) { th.AssertNoErr(t, err) th.CheckDeepEquals(t, MappingUpdated, *actual) } + +func TestDeleteMapping(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleDeleteMappingSuccessfully(t) + + res := federation.DeleteMapping(client.ServiceClient(), "ACME") + th.AssertNoErr(t, res.Err) +} From a8ce133cdf4879929357cc75bb41cbabc203acc9 Mon Sep 17 00:00:00 2001 From: kayrus <kay.diam@gmail.com> Date: Sun, 5 Mar 2023 13:44:42 +0100 Subject: [PATCH 213/360] [neutron v2]: Add support for network segments update --- .../v2/extensions/provider/requests.go | 23 ++++++++++++++ .../provider/testing/results_test.go | 31 ++++++++++++++++--- 2 files changed, 49 insertions(+), 5 deletions(-) diff --git a/openstack/networking/v2/extensions/provider/requests.go b/openstack/networking/v2/extensions/provider/requests.go index 32c27970a4..d4f3c60a30 100644 --- a/openstack/networking/v2/extensions/provider/requests.go +++ b/openstack/networking/v2/extensions/provider/requests.go @@ -26,3 +26,26 @@ func (opts CreateOptsExt) ToNetworkCreateMap() (map[string]interface{}, error) { return base, nil } + +// UpdateOptsExt adds a Segments option to the base Network UpdateOpts. +type UpdateOptsExt struct { + networks.UpdateOptsBuilder + Segments *[]Segment `json:"segments,omitempty"` +} + +// ToNetworkUpdateMap adds segments to the base network update options. +func (opts UpdateOptsExt) ToNetworkUpdateMap() (map[string]interface{}, error) { + base, err := opts.UpdateOptsBuilder.ToNetworkUpdateMap() + if err != nil { + return nil, err + } + + if opts.Segments == nil { + return base, nil + } + + providerMap := base["network"].(map[string]interface{}) + providerMap["segments"] = opts.Segments + + return base, nil +} diff --git a/openstack/networking/v2/extensions/provider/testing/results_test.go b/openstack/networking/v2/extensions/provider/testing/results_test.go index fe5ea98973..16fe0848b8 100644 --- a/openstack/networking/v2/extensions/provider/testing/results_test.go +++ b/openstack/networking/v2/extensions/provider/testing/results_test.go @@ -192,12 +192,36 @@ func TestUpdate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() + iTrue := true + name := "new_network_name" + segments := []provider.Segment{ + {NetworkType: "vxlan", PhysicalNetwork: "br-ex", SegmentationID: 615}, + } + networkUpdateOpts := networks.UpdateOpts{Name: &name, AdminStateUp: gophercloud.Disabled, Shared: &iTrue} + providerUpdateOpts := provider.UpdateOptsExt{ + UpdateOptsBuilder: networkUpdateOpts, + Segments: &segments, + } + th.Mux.HandleFunc("/v2.0/networks/4e8e5957-649f-477b-9e5b-f1f75b21c03c", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") - th.TestJSONRequest(t, r, nettest.UpdateRequest) + th.TestJSONRequest(t, r, `{ + "network": { + "admin_state_up": false, + "name": "new_network_name", + "segments": [ + { + "provider:network_type": "vxlan", + "provider:physical_network": "br-ex", + "provider:segmentation_id": 615 + } + ], + "shared": true + } +}`) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) @@ -210,10 +234,7 @@ func TestUpdate(t *testing.T) { provider.NetworkProviderExt } - iTrue := true - name := "new_network_name" - options := networks.UpdateOpts{Name: &name, AdminStateUp: gophercloud.Disabled, Shared: &iTrue} - err := networks.Update(fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c", options).ExtractInto(&s) + err := networks.Update(fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c", providerUpdateOpts).ExtractInto(&s) th.AssertNoErr(t, err) th.AssertEquals(t, "4e8e5957-649f-477b-9e5b-f1f75b21c03c", s.ID) From 974f42614020e76d8973f3f485225c593da2f3cd Mon Sep 17 00:00:00 2001 From: Pierre Prinetti <pierreprinetti@redhat.com> Date: Thu, 26 Jan 2023 10:46:24 +0100 Subject: [PATCH 214/360] [all] IsEmpty to check for HTTP status 204 With this commit, all `IsEmpty() (bool, error)` functions now check the status code of the result. If it's `204`, they immediately confirm that the page is empty. This change was introduced in commit 64ed1bc1 in `objectstorage` to support Swift instances running behind a reverse proxy that was truncating `content-type` headers from `204 Empty` responses. With this change, the same check is applied to all services. While no functional impact is expected on non-proxied OpenStack modules, this patch will prevent issues on proxied clouds. In general, it also seems reasonable to expect `204 Empty` responses to be empty. --- This change is the result of running: ```shell find -name 'results.go' -exec sed -i 's|^\(func (\(.\+\) .\+) IsEmpty() (bool, error) {\)$|\1\nif \2.StatusCode==204 {\nreturn true,nil\n}\n|' {} \; go fmt ./... ``` --- docs/contributor-tutorial/.template/results.go | 4 ++++ openstack/baremetal/v1/allocations/results.go | 4 ++++ openstack/baremetal/v1/drivers/results.go | 4 ++++ openstack/baremetal/v1/nodes/results.go | 4 ++++ openstack/baremetal/v1/ports/results.go | 4 ++++ .../v1/introspection/results.go | 4 ++++ openstack/blockstorage/apiversions/results.go | 4 ++++ openstack/blockstorage/extensions/backups/results.go | 4 ++++ .../blockstorage/extensions/quotasets/results.go | 4 ++++ .../extensions/schedulerstats/results.go | 4 ++++ .../blockstorage/extensions/services/results.go | 4 ++++ .../extensions/volumetransfers/results.go | 4 ++++ openstack/blockstorage/v1/apiversions/results.go | 4 ++++ openstack/blockstorage/v1/snapshots/results.go | 4 ++++ openstack/blockstorage/v1/volumes/results.go | 4 ++++ openstack/blockstorage/v1/volumetypes/results.go | 4 ++++ openstack/blockstorage/v2/snapshots/results.go | 4 ++++ openstack/blockstorage/v2/volumes/results.go | 4 ++++ openstack/blockstorage/v3/attachments/results.go | 4 ++++ openstack/blockstorage/v3/qos/results.go | 8 ++++++++ openstack/blockstorage/v3/snapshots/results.go | 4 ++++ openstack/blockstorage/v3/volumes/results.go | 4 ++++ openstack/blockstorage/v3/volumetypes/results.go | 8 ++++++++ openstack/cdn/v1/flavors/results.go | 4 ++++ openstack/cdn/v1/services/results.go | 4 ++++ openstack/clustering/v1/actions/results.go | 4 ++++ openstack/clustering/v1/clusters/results.go | 8 ++++++++ openstack/clustering/v1/events/results.go | 4 ++++ openstack/clustering/v1/nodes/results.go | 4 ++++ openstack/clustering/v1/policies/results.go | 4 ++++ openstack/clustering/v1/policytypes/results.go | 4 ++++ openstack/clustering/v1/profiles/results.go | 4 ++++ openstack/clustering/v1/profiletypes/results.go | 4 ++++ openstack/clustering/v1/receivers/results.go | 4 ++++ openstack/common/extensions/results.go | 4 ++++ openstack/compute/apiversions/results.go | 4 ++++ .../compute/v2/extensions/aggregates/results.go | 4 ++++ .../v2/extensions/attachinterfaces/results.go | 4 ++++ .../compute/v2/extensions/defsecrules/results.go | 4 ++++ .../compute/v2/extensions/floatingips/results.go | 4 ++++ .../compute/v2/extensions/hypervisors/results.go | 4 ++++ .../compute/v2/extensions/instanceactions/results.go | 4 ++++ openstack/compute/v2/extensions/keypairs/results.go | 4 ++++ openstack/compute/v2/extensions/networks/results.go | 4 ++++ openstack/compute/v2/extensions/quotasets/results.go | 4 ++++ openstack/compute/v2/extensions/secgroups/results.go | 4 ++++ .../compute/v2/extensions/servergroups/results.go | 4 ++++ openstack/compute/v2/extensions/services/results.go | 4 ++++ .../compute/v2/extensions/tenantnetworks/results.go | 4 ++++ openstack/compute/v2/extensions/usage/results.go | 8 ++++++++ .../compute/v2/extensions/volumeattach/results.go | 4 ++++ openstack/compute/v2/flavors/results.go | 8 ++++++++ openstack/compute/v2/images/results.go | 4 ++++ openstack/compute/v2/servers/results.go | 12 ++++++++++++ openstack/container/v1/capsules/results.go | 4 ++++ openstack/containerinfra/apiversions/results.go | 4 ++++ openstack/containerinfra/v1/clusters/results.go | 4 ++++ .../containerinfra/v1/clustertemplates/results.go | 4 ++++ openstack/containerinfra/v1/nodegroups/results.go | 4 ++++ openstack/db/v1/configurations/results.go | 8 ++++++++ openstack/db/v1/databases/results.go | 4 ++++ openstack/db/v1/datastores/results.go | 8 ++++++++ openstack/db/v1/flavors/results.go | 4 ++++ openstack/db/v1/instances/results.go | 4 ++++ openstack/db/v1/users/results.go | 4 ++++ openstack/dns/v2/recordsets/results.go | 4 ++++ openstack/dns/v2/transfer/accept/results.go | 4 ++++ openstack/dns/v2/transfer/request/results.go | 4 ++++ openstack/dns/v2/zones/results.go | 4 ++++ .../identity/v2/extensions/admin/roles/results.go | 4 ++++ openstack/identity/v2/tenants/results.go | 4 ++++ openstack/identity/v2/users/results.go | 8 ++++++++ .../identity/v3/applicationcredentials/results.go | 8 ++++++++ openstack/identity/v3/catalog/results.go | 4 ++++ openstack/identity/v3/credentials/results.go | 4 ++++ openstack/identity/v3/domains/results.go | 4 ++++ openstack/identity/v3/endpoints/results.go | 4 ++++ .../identity/v3/extensions/ec2credentials/results.go | 4 ++++ .../identity/v3/extensions/federation/results.go | 4 ++++ openstack/identity/v3/extensions/oauth1/results.go | 12 ++++++++++++ .../v3/extensions/projectendpoints/results.go | 4 ++++ openstack/identity/v3/extensions/trusts/results.go | 8 ++++++++ openstack/identity/v3/groups/results.go | 4 ++++ openstack/identity/v3/limits/results.go | 4 ++++ openstack/identity/v3/policies/results.go | 4 ++++ openstack/identity/v3/projects/results.go | 4 ++++ openstack/identity/v3/regions/results.go | 4 ++++ openstack/identity/v3/roles/results.go | 8 ++++++++ openstack/identity/v3/services/results.go | 4 ++++ openstack/identity/v3/users/results.go | 4 ++++ openstack/imageservice/v2/images/results.go | 4 ++++ openstack/imageservice/v2/members/results.go | 4 ++++ openstack/imageservice/v2/tasks/results.go | 4 ++++ openstack/keymanager/v1/containers/results.go | 8 ++++++++ openstack/keymanager/v1/orders/results.go | 4 ++++ openstack/keymanager/v1/secrets/results.go | 4 ++++ openstack/loadbalancer/v2/amphorae/results.go | 4 ++++ openstack/loadbalancer/v2/apiversions/results.go | 4 ++++ openstack/loadbalancer/v2/l7policies/results.go | 8 ++++++++ openstack/loadbalancer/v2/listeners/results.go | 4 ++++ openstack/loadbalancer/v2/loadbalancers/results.go | 4 ++++ openstack/loadbalancer/v2/monitors/results.go | 4 ++++ openstack/loadbalancer/v2/pools/results.go | 8 ++++++++ openstack/loadbalancer/v2/providers/results.go | 4 ++++ openstack/messaging/v2/messages/results.go | 4 ++++ openstack/messaging/v2/queues/results.go | 4 ++++ openstack/networking/v2/apiversions/results.go | 8 ++++++++ openstack/networking/v2/extensions/agents/results.go | 8 ++++++++ .../networking/v2/extensions/bgp/peers/results.go | 4 ++++ .../networking/v2/extensions/bgp/speakers/results.go | 8 ++++++++ .../v2/extensions/fwaas/firewalls/results.go | 4 ++++ .../v2/extensions/fwaas/policies/results.go | 4 ++++ .../networking/v2/extensions/fwaas/rules/results.go | 4 ++++ .../v2/extensions/fwaas_v2/groups/results.go | 4 ++++ .../v2/extensions/fwaas_v2/policies/results.go | 4 ++++ .../v2/extensions/fwaas_v2/rules/results.go | 4 ++++ .../v2/extensions/layer3/addressscopes/results.go | 4 ++++ .../v2/extensions/layer3/floatingips/results.go | 4 ++++ .../v2/extensions/layer3/portforwarding/results.go | 4 ++++ .../v2/extensions/layer3/routers/results.go | 8 ++++++++ .../v2/extensions/lbaas/members/results.go | 4 ++++ .../v2/extensions/lbaas/monitors/results.go | 4 ++++ .../networking/v2/extensions/lbaas/pools/results.go | 4 ++++ .../networking/v2/extensions/lbaas/vips/results.go | 4 ++++ .../v2/extensions/lbaas_v2/l7policies/results.go | 8 ++++++++ .../v2/extensions/lbaas_v2/listeners/results.go | 4 ++++ .../v2/extensions/lbaas_v2/loadbalancers/results.go | 4 ++++ .../v2/extensions/lbaas_v2/monitors/results.go | 4 ++++ .../v2/extensions/lbaas_v2/pools/results.go | 8 ++++++++ .../v2/extensions/networkipavailabilities/results.go | 4 ++++ .../networking/v2/extensions/qos/policies/results.go | 4 ++++ .../networking/v2/extensions/qos/rules/results.go | 12 ++++++++++++ .../v2/extensions/qos/ruletypes/results.go | 4 ++++ .../networking/v2/extensions/rbacpolicies/results.go | 4 ++++ .../v2/extensions/security/groups/results.go | 4 ++++ .../v2/extensions/security/rules/results.go | 4 ++++ .../networking/v2/extensions/subnetpools/results.go | 4 ++++ openstack/networking/v2/extensions/trunks/results.go | 4 ++++ .../v2/extensions/vpnaas/endpointgroups/results.go | 4 ++++ .../v2/extensions/vpnaas/ikepolicies/results.go | 4 ++++ .../v2/extensions/vpnaas/ipsecpolicies/results.go | 4 ++++ .../v2/extensions/vpnaas/services/results.go | 4 ++++ .../v2/extensions/vpnaas/siteconnections/results.go | 4 ++++ openstack/networking/v2/networks/results.go | 4 ++++ openstack/networking/v2/ports/results.go | 4 ++++ openstack/networking/v2/subnets/results.go | 4 ++++ openstack/orchestration/v1/apiversions/results.go | 4 ++++ openstack/orchestration/v1/stackevents/results.go | 4 ++++ openstack/orchestration/v1/stackresources/results.go | 8 ++++++++ openstack/orchestration/v1/stacks/results.go | 4 ++++ openstack/placement/v1/resourceproviders/results.go | 4 ++++ openstack/sharedfilesystems/apiversions/results.go | 4 ++++ openstack/sharedfilesystems/v2/messages/results.go | 4 ++++ .../sharedfilesystems/v2/schedulerstats/results.go | 4 ++++ .../sharedfilesystems/v2/securityservices/results.go | 4 ++++ openstack/sharedfilesystems/v2/services/results.go | 4 ++++ .../sharedfilesystems/v2/sharenetworks/results.go | 4 ++++ openstack/sharedfilesystems/v2/shares/results.go | 4 ++++ openstack/sharedfilesystems/v2/sharetypes/results.go | 4 ++++ openstack/sharedfilesystems/v2/snapshots/results.go | 4 ++++ openstack/workflow/v2/crontriggers/results.go | 4 ++++ openstack/workflow/v2/executions/results.go | 4 ++++ openstack/workflow/v2/workflows/results.go | 4 ++++ 163 files changed, 760 insertions(+) diff --git a/docs/contributor-tutorial/.template/results.go b/docs/contributor-tutorial/.template/results.go index 88272e1e1c..0ff0c10500 100644 --- a/docs/contributor-tutorial/.template/results.go +++ b/docs/contributor-tutorial/.template/results.go @@ -44,6 +44,10 @@ type ResourcePage struct { // IsEmpty determines whether or not a page of RESOURCES contains any results. func (r ResourcePage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + resources, err := ExtractResources(r) return len(resources) == 0, err } diff --git a/openstack/baremetal/v1/allocations/results.go b/openstack/baremetal/v1/allocations/results.go index cbd2115523..6614ea1587 100644 --- a/openstack/baremetal/v1/allocations/results.go +++ b/openstack/baremetal/v1/allocations/results.go @@ -71,6 +71,10 @@ type AllocationPage struct { // IsEmpty returns true if a page contains no Allocation results. func (r AllocationPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + s, err := ExtractAllocations(r) return len(s) == 0, err } diff --git a/openstack/baremetal/v1/drivers/results.go b/openstack/baremetal/v1/drivers/results.go index 424079c8ea..8ef42459d1 100644 --- a/openstack/baremetal/v1/drivers/results.go +++ b/openstack/baremetal/v1/drivers/results.go @@ -134,6 +134,10 @@ type DriverPage struct { // IsEmpty returns true if a page contains no Driver results. func (r DriverPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + s, err := ExtractDrivers(r) return len(s) == 0, err } diff --git a/openstack/baremetal/v1/nodes/results.go b/openstack/baremetal/v1/nodes/results.go index f1996ae672..3756789f87 100644 --- a/openstack/baremetal/v1/nodes/results.go +++ b/openstack/baremetal/v1/nodes/results.go @@ -263,6 +263,10 @@ type NodePage struct { // IsEmpty returns true if a page contains no Node results. func (r NodePage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + s, err := ExtractNodes(r) return len(s) == 0, err } diff --git a/openstack/baremetal/v1/ports/results.go b/openstack/baremetal/v1/ports/results.go index 506b6c64a7..226bd7ada4 100644 --- a/openstack/baremetal/v1/ports/results.go +++ b/openstack/baremetal/v1/ports/results.go @@ -82,6 +82,10 @@ type PortPage struct { // IsEmpty returns true if a page contains no Port results. func (r PortPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + s, err := ExtractPorts(r) return len(s) == 0, err } diff --git a/openstack/baremetalintrospection/v1/introspection/results.go b/openstack/baremetalintrospection/v1/introspection/results.go index e9abe85328..43cc53c299 100644 --- a/openstack/baremetalintrospection/v1/introspection/results.go +++ b/openstack/baremetalintrospection/v1/introspection/results.go @@ -73,6 +73,10 @@ type Introspection struct { // IsEmpty returns true if a page contains no Introspection results. func (r IntrospectionPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + s, err := ExtractIntrospections(r) return len(s) == 0, err } diff --git a/openstack/blockstorage/apiversions/results.go b/openstack/blockstorage/apiversions/results.go index 08adcb72ca..941dca1813 100644 --- a/openstack/blockstorage/apiversions/results.go +++ b/openstack/blockstorage/apiversions/results.go @@ -32,6 +32,10 @@ type APIVersionPage struct { // IsEmpty checks whether an APIVersionPage struct is empty. func (r APIVersionPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + is, err := ExtractAPIVersions(r) return len(is) == 0, err } diff --git a/openstack/blockstorage/extensions/backups/results.go b/openstack/blockstorage/extensions/backups/results.go index a575498de2..520a1ff486 100644 --- a/openstack/blockstorage/extensions/backups/results.go +++ b/openstack/blockstorage/extensions/backups/results.go @@ -113,6 +113,10 @@ func (r *Backup) UnmarshalJSON(b []byte) error { // IsEmpty returns true if a BackupPage contains no Backups. func (r BackupPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + volumes, err := ExtractBackups(r) return len(volumes) == 0, err } diff --git a/openstack/blockstorage/extensions/quotasets/results.go b/openstack/blockstorage/extensions/quotasets/results.go index bc516be5da..3a0a3e9293 100644 --- a/openstack/blockstorage/extensions/quotasets/results.go +++ b/openstack/blockstorage/extensions/quotasets/results.go @@ -139,6 +139,10 @@ type QuotaSetPage struct { // IsEmpty determines whether or not a QuotaSetsetPage is empty. func (r QuotaSetPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + ks, err := ExtractQuotaSets(r) return len(ks) == 0, err } diff --git a/openstack/blockstorage/extensions/schedulerstats/results.go b/openstack/blockstorage/extensions/schedulerstats/results.go index 6729907130..9bf19a9fba 100644 --- a/openstack/blockstorage/extensions/schedulerstats/results.go +++ b/openstack/blockstorage/extensions/schedulerstats/results.go @@ -97,6 +97,10 @@ type StoragePoolPage struct { // IsEmpty satisfies the IsEmpty method of the Page interface. It returns true // if a List contains no results. func (page StoragePoolPage) IsEmpty() (bool, error) { + if page.StatusCode == 204 { + return true, nil + } + va, err := ExtractStoragePools(page) return len(va) == 0, err } diff --git a/openstack/blockstorage/extensions/services/results.go b/openstack/blockstorage/extensions/services/results.go index 49ad48ef61..bf0160423d 100644 --- a/openstack/blockstorage/extensions/services/results.go +++ b/openstack/blockstorage/extensions/services/results.go @@ -71,6 +71,10 @@ type ServicePage struct { // IsEmpty determines whether or not a page of Services contains any results. func (page ServicePage) IsEmpty() (bool, error) { + if page.StatusCode == 204 { + return true, nil + } + services, err := ExtractServices(page) return len(services) == 0, err } diff --git a/openstack/blockstorage/extensions/volumetransfers/results.go b/openstack/blockstorage/extensions/volumetransfers/results.go index 3217174a8a..19b65c44cf 100644 --- a/openstack/blockstorage/extensions/volumetransfers/results.go +++ b/openstack/blockstorage/extensions/volumetransfers/results.go @@ -86,6 +86,10 @@ type TransferPage struct { // IsEmpty returns true if a ListResult contains no Transfers. func (r TransferPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + transfers, err := ExtractTransfers(r) return len(transfers) == 0, err } diff --git a/openstack/blockstorage/v1/apiversions/results.go b/openstack/blockstorage/v1/apiversions/results.go index f510c6d103..1e176d25b3 100644 --- a/openstack/blockstorage/v1/apiversions/results.go +++ b/openstack/blockstorage/v1/apiversions/results.go @@ -20,6 +20,10 @@ type APIVersionPage struct { // IsEmpty checks whether an APIVersionPage struct is empty. func (r APIVersionPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + is, err := ExtractAPIVersions(r) return len(is) == 0, err } diff --git a/openstack/blockstorage/v1/snapshots/results.go b/openstack/blockstorage/v1/snapshots/results.go index 5282509273..ca2445c805 100644 --- a/openstack/blockstorage/v1/snapshots/results.go +++ b/openstack/blockstorage/v1/snapshots/results.go @@ -89,6 +89,10 @@ type SnapshotPage struct { // IsEmpty returns true if a SnapshotPage contains no Snapshots. func (r SnapshotPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + volumes, err := ExtractSnapshots(r) return len(volumes) == 0, err } diff --git a/openstack/blockstorage/v1/volumes/results.go b/openstack/blockstorage/v1/volumes/results.go index 7f68d14863..a72028479b 100644 --- a/openstack/blockstorage/v1/volumes/results.go +++ b/openstack/blockstorage/v1/volumes/results.go @@ -77,6 +77,10 @@ type VolumePage struct { // IsEmpty returns true if a VolumePage 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 } diff --git a/openstack/blockstorage/v1/volumetypes/results.go b/openstack/blockstorage/v1/volumetypes/results.go index 2c312385c6..66cffe3e1f 100644 --- a/openstack/blockstorage/v1/volumetypes/results.go +++ b/openstack/blockstorage/v1/volumetypes/results.go @@ -34,6 +34,10 @@ type VolumeTypePage struct { // IsEmpty returns true if a VolumeTypePage contains no Volume Types. func (r VolumeTypePage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + volumeTypes, err := ExtractVolumeTypes(r) return len(volumeTypes) == 0, err } diff --git a/openstack/blockstorage/v2/snapshots/results.go b/openstack/blockstorage/v2/snapshots/results.go index 0b444d08ad..5450fc2876 100644 --- a/openstack/blockstorage/v2/snapshots/results.go +++ b/openstack/blockstorage/v2/snapshots/results.go @@ -79,6 +79,10 @@ func (r *Snapshot) UnmarshalJSON(b []byte) error { // IsEmpty returns true if a SnapshotPage contains no Snapshots. func (r SnapshotPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + volumes, err := ExtractSnapshots(r) return len(volumes) == 0, err } diff --git a/openstack/blockstorage/v2/volumes/results.go b/openstack/blockstorage/v2/volumes/results.go index 96572b01b4..4dd5d6c077 100644 --- a/openstack/blockstorage/v2/volumes/results.go +++ b/openstack/blockstorage/v2/volumes/results.go @@ -103,6 +103,10 @@ type VolumePage struct { // 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 } diff --git a/openstack/blockstorage/v3/attachments/results.go b/openstack/blockstorage/v3/attachments/results.go index 0a97730ed2..791368ae78 100644 --- a/openstack/blockstorage/v3/attachments/results.go +++ b/openstack/blockstorage/v3/attachments/results.go @@ -60,6 +60,10 @@ type AttachmentPage struct { // IsEmpty returns true if a ListResult contains no Attachments. func (r AttachmentPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + attachments, err := ExtractAttachments(r) return len(attachments) == 0, err } diff --git a/openstack/blockstorage/v3/qos/results.go b/openstack/blockstorage/v3/qos/results.go index 34008aed80..d5f4254d2b 100644 --- a/openstack/blockstorage/v3/qos/results.go +++ b/openstack/blockstorage/v3/qos/results.go @@ -49,6 +49,10 @@ type QoSPage struct { // IsEmpty determines if a QoSPage contains any results. func (page QoSPage) IsEmpty() (bool, error) { + if page.StatusCode == 204 { + return true, nil + } + qos, err := ExtractQoS(page) return len(qos) == 0, err } @@ -130,6 +134,10 @@ type AssociationPage struct { // IsEmpty indicates whether an Association page is empty. func (page AssociationPage) IsEmpty() (bool, error) { + if page.StatusCode == 204 { + return true, nil + } + v, err := ExtractAssociations(page) return len(v) == 0, err } diff --git a/openstack/blockstorage/v3/snapshots/results.go b/openstack/blockstorage/v3/snapshots/results.go index 3c81a447a6..d058b22172 100644 --- a/openstack/blockstorage/v3/snapshots/results.go +++ b/openstack/blockstorage/v3/snapshots/results.go @@ -85,6 +85,10 @@ func (r *Snapshot) UnmarshalJSON(b []byte) error { // IsEmpty returns true if a SnapshotPage contains no Snapshots. func (r SnapshotPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + volumes, err := ExtractSnapshots(r) return len(volumes) == 0, err } diff --git a/openstack/blockstorage/v3/volumes/results.go b/openstack/blockstorage/v3/volumes/results.go index 6f46685b6e..df97e66947 100644 --- a/openstack/blockstorage/v3/volumes/results.go +++ b/openstack/blockstorage/v3/volumes/results.go @@ -111,6 +111,10 @@ type VolumePage struct { // 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 } diff --git a/openstack/blockstorage/v3/volumetypes/results.go b/openstack/blockstorage/v3/volumetypes/results.go index 72c696ef13..916b476da5 100644 --- a/openstack/blockstorage/v3/volumetypes/results.go +++ b/openstack/blockstorage/v3/volumetypes/results.go @@ -30,6 +30,10 @@ type VolumeTypePage struct { // IsEmpty returns true if a ListResult contains no Volume Types. func (r VolumeTypePage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + volumetypes, err := ExtractVolumeTypes(r) return len(volumetypes) == 0, err } @@ -168,6 +172,10 @@ type AccessPage struct { // IsEmpty indicates whether an AccessPage is empty. func (page AccessPage) IsEmpty() (bool, error) { + if page.StatusCode == 204 { + return true, nil + } + v, err := ExtractAccesses(page) return len(v) == 0, err } diff --git a/openstack/cdn/v1/flavors/results.go b/openstack/cdn/v1/flavors/results.go index 02c285134b..f7222c75cf 100644 --- a/openstack/cdn/v1/flavors/results.go +++ b/openstack/cdn/v1/flavors/results.go @@ -33,6 +33,10 @@ type FlavorPage struct { // IsEmpty returns true if a FlavorPage contains no Flavors. func (r FlavorPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + flavors, err := ExtractFlavors(r) return len(flavors) == 0, err } diff --git a/openstack/cdn/v1/services/results.go b/openstack/cdn/v1/services/results.go index f9a1caae73..fd77f192f8 100644 --- a/openstack/cdn/v1/services/results.go +++ b/openstack/cdn/v1/services/results.go @@ -229,6 +229,10 @@ type ServicePage struct { // IsEmpty returns true if a ListResult contains no services. func (r ServicePage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + services, err := ExtractServices(r) return len(services) == 0, err } diff --git a/openstack/clustering/v1/actions/results.go b/openstack/clustering/v1/actions/results.go index ca03a4b71c..c73371bd56 100644 --- a/openstack/clustering/v1/actions/results.go +++ b/openstack/clustering/v1/actions/results.go @@ -92,6 +92,10 @@ type ActionPage struct { // IsEmpty determines if a ActionPage contains any results. func (r ActionPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + actions, err := ExtractActions(r) return len(actions) == 0, err } diff --git a/openstack/clustering/v1/clusters/results.go b/openstack/clustering/v1/clusters/results.go index 8548d4bb4c..23bc8ca1ae 100644 --- a/openstack/clustering/v1/clusters/results.go +++ b/openstack/clustering/v1/clusters/results.go @@ -155,6 +155,10 @@ type ClusterPage struct { // IsEmpty determines whether or not a page of Clusters contains any results. func (page ClusterPage) IsEmpty() (bool, error) { + if page.StatusCode == 204 { + return true, nil + } + clusters, err := ExtractClusters(page) return len(clusters) == 0, err } @@ -167,6 +171,10 @@ type ClusterPolicyPage struct { // IsEmpty determines whether or not a page of ClusterPolicies contains any // results. func (page ClusterPolicyPage) IsEmpty() (bool, error) { + if page.StatusCode == 204 { + return true, nil + } + clusterPolicies, err := ExtractClusterPolicies(page) return len(clusterPolicies) == 0, err } diff --git a/openstack/clustering/v1/events/results.go b/openstack/clustering/v1/events/results.go index b8542d2685..abec9db307 100644 --- a/openstack/clustering/v1/events/results.go +++ b/openstack/clustering/v1/events/results.go @@ -52,6 +52,10 @@ type EventPage struct { // IsEmpty determines if a EventPage contains any results. func (r EventPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + events, err := ExtractEvents(r) return len(events) == 0, err } diff --git a/openstack/clustering/v1/nodes/results.go b/openstack/clustering/v1/nodes/results.go index 8921a7649e..fdee5d7258 100644 --- a/openstack/clustering/v1/nodes/results.go +++ b/openstack/clustering/v1/nodes/results.go @@ -115,6 +115,10 @@ type NodePage struct { // IsEmpty determines if a NodePage contains any results. func (page NodePage) IsEmpty() (bool, error) { + if page.StatusCode == 204 { + return true, nil + } + nodes, err := ExtractNodes(page) return len(nodes) == 0, err } diff --git a/openstack/clustering/v1/policies/results.go b/openstack/clustering/v1/policies/results.go index 89b565aaf9..f8553b28ef 100644 --- a/openstack/clustering/v1/policies/results.go +++ b/openstack/clustering/v1/policies/results.go @@ -159,6 +159,10 @@ type PolicyPage struct { // IsEmpty determines if a PolicyPage contains any results. func (page PolicyPage) IsEmpty() (bool, error) { + if page.StatusCode == 204 { + return true, nil + } + policies, err := ExtractPolicies(page) return len(policies) == 0, err } diff --git a/openstack/clustering/v1/policytypes/results.go b/openstack/clustering/v1/policytypes/results.go index 98c1e53cb7..ce6fa75dfd 100644 --- a/openstack/clustering/v1/policytypes/results.go +++ b/openstack/clustering/v1/policytypes/results.go @@ -54,6 +54,10 @@ type PolicyTypePage struct { // IsEmpty determines if a PolicyType contains any results. func (page PolicyTypePage) IsEmpty() (bool, error) { + if page.StatusCode == 204 { + return true, nil + } + policyTypes, err := ExtractPolicyTypes(page) return len(policyTypes) == 0, err } diff --git a/openstack/clustering/v1/profiles/results.go b/openstack/clustering/v1/profiles/results.go index 8ce15bad82..a699aeb202 100644 --- a/openstack/clustering/v1/profiles/results.go +++ b/openstack/clustering/v1/profiles/results.go @@ -152,6 +152,10 @@ type ProfilePage struct { // IsEmpty determines if a ProfilePage contains any results. func (page ProfilePage) IsEmpty() (bool, error) { + if page.StatusCode == 204 { + return true, nil + } + profiles, err := ExtractProfiles(page) return len(profiles) == 0, err } diff --git a/openstack/clustering/v1/profiletypes/results.go b/openstack/clustering/v1/profiletypes/results.go index 5364727f67..b4b1c0bd52 100644 --- a/openstack/clustering/v1/profiletypes/results.go +++ b/openstack/clustering/v1/profiletypes/results.go @@ -48,6 +48,10 @@ type ProfileTypePage struct { // IsEmpty determines if ExtractProfileTypes contains any results. func (page ProfileTypePage) IsEmpty() (bool, error) { + if page.StatusCode == 204 { + return true, nil + } + profileTypes, err := ExtractProfileTypes(page) return len(profileTypes) == 0, err } diff --git a/openstack/clustering/v1/receivers/results.go b/openstack/clustering/v1/receivers/results.go index a25192b6a9..4b3b064f98 100644 --- a/openstack/clustering/v1/receivers/results.go +++ b/openstack/clustering/v1/receivers/results.go @@ -106,6 +106,10 @@ type ReceiverPage struct { // IsEmpty determines if a ReceiverPage contains any results. func (page ReceiverPage) IsEmpty() (bool, error) { + if page.StatusCode == 204 { + return true, nil + } + receivers, err := ExtractReceivers(page) return len(receivers) == 0, err } diff --git a/openstack/common/extensions/results.go b/openstack/common/extensions/results.go index 8a26edd1c5..609d132e0a 100644 --- a/openstack/common/extensions/results.go +++ b/openstack/common/extensions/results.go @@ -37,6 +37,10 @@ type ExtensionPage struct { // IsEmpty checks whether an ExtensionPage struct is empty. func (r ExtensionPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + is, err := ExtractExtensions(r) return len(is) == 0, err } diff --git a/openstack/compute/apiversions/results.go b/openstack/compute/apiversions/results.go index 1459e7452b..3718172463 100644 --- a/openstack/compute/apiversions/results.go +++ b/openstack/compute/apiversions/results.go @@ -33,6 +33,10 @@ type APIVersionPage struct { // IsEmpty checks whether an APIVersionPage struct is empty. func (r APIVersionPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + is, err := ExtractAPIVersions(r) return len(is) == 0, err } diff --git a/openstack/compute/v2/extensions/aggregates/results.go b/openstack/compute/v2/extensions/aggregates/results.go index 2ab0cf22f0..96a7b740c5 100644 --- a/openstack/compute/v2/extensions/aggregates/results.go +++ b/openstack/compute/v2/extensions/aggregates/results.go @@ -71,6 +71,10 @@ type AggregatesPage struct { // IsEmpty determines whether or not a page of Aggregates contains any results. func (page AggregatesPage) IsEmpty() (bool, error) { + if page.StatusCode == 204 { + return true, nil + } + aggregates, err := ExtractAggregates(page) return len(aggregates) == 0, err } diff --git a/openstack/compute/v2/extensions/attachinterfaces/results.go b/openstack/compute/v2/extensions/attachinterfaces/results.go index 7d15e1ecb4..e713c34e9b 100644 --- a/openstack/compute/v2/extensions/attachinterfaces/results.go +++ b/openstack/compute/v2/extensions/attachinterfaces/results.go @@ -65,6 +65,10 @@ type InterfacePage struct { // IsEmpty returns true if an InterfacePage contains no interfaces. func (r InterfacePage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + interfaces, err := ExtractInterfaces(r) return len(interfaces) == 0, err } diff --git a/openstack/compute/v2/extensions/defsecrules/results.go b/openstack/compute/v2/extensions/defsecrules/results.go index 98c18fe560..36035acc2a 100644 --- a/openstack/compute/v2/extensions/defsecrules/results.go +++ b/openstack/compute/v2/extensions/defsecrules/results.go @@ -29,6 +29,10 @@ type DefaultRulePage struct { // IsEmpty determines whether or not a page of default rules contains any results. func (page DefaultRulePage) IsEmpty() (bool, error) { + if page.StatusCode == 204 { + return true, nil + } + users, err := ExtractDefaultRules(page) return len(users) == 0, err } diff --git a/openstack/compute/v2/extensions/floatingips/results.go b/openstack/compute/v2/extensions/floatingips/results.go index da4e9da0e6..4009b8336b 100644 --- a/openstack/compute/v2/extensions/floatingips/results.go +++ b/openstack/compute/v2/extensions/floatingips/results.go @@ -56,6 +56,10 @@ type FloatingIPPage struct { // IsEmpty determines whether or not a FloatingIPsPage is empty. func (page FloatingIPPage) IsEmpty() (bool, error) { + if page.StatusCode == 204 { + return true, nil + } + va, err := ExtractFloatingIPs(page) return len(va) == 0, err } diff --git a/openstack/compute/v2/extensions/hypervisors/results.go b/openstack/compute/v2/extensions/hypervisors/results.go index 6c172b4cac..26113f348e 100644 --- a/openstack/compute/v2/extensions/hypervisors/results.go +++ b/openstack/compute/v2/extensions/hypervisors/results.go @@ -235,6 +235,10 @@ type HypervisorPage struct { // IsEmpty determines whether or not a HypervisorPage is empty. func (page HypervisorPage) IsEmpty() (bool, error) { + if page.StatusCode == 204 { + return true, nil + } + va, err := ExtractHypervisors(page) return len(va) == 0, err } diff --git a/openstack/compute/v2/extensions/instanceactions/results.go b/openstack/compute/v2/extensions/instanceactions/results.go index 90892a29a0..351d4b1eeb 100644 --- a/openstack/compute/v2/extensions/instanceactions/results.go +++ b/openstack/compute/v2/extensions/instanceactions/results.go @@ -60,6 +60,10 @@ type InstanceActionPage struct { // IsEmpty returns true if an InstanceActionPage contains no instance actions. func (r InstanceActionPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + instanceactions, err := ExtractInstanceActions(r) return len(instanceactions) == 0, err } diff --git a/openstack/compute/v2/extensions/keypairs/results.go b/openstack/compute/v2/extensions/keypairs/results.go index 6e8db69954..0ac05c361d 100644 --- a/openstack/compute/v2/extensions/keypairs/results.go +++ b/openstack/compute/v2/extensions/keypairs/results.go @@ -41,6 +41,10 @@ type KeyPairPage struct { // IsEmpty determines whether or not a KeyPairPage is empty. func (page KeyPairPage) IsEmpty() (bool, error) { + if page.StatusCode == 204 { + return true, nil + } + ks, err := ExtractKeyPairs(page) return len(ks) == 0, err } diff --git a/openstack/compute/v2/extensions/networks/results.go b/openstack/compute/v2/extensions/networks/results.go index c36ce678cc..b6c4158f43 100644 --- a/openstack/compute/v2/extensions/networks/results.go +++ b/openstack/compute/v2/extensions/networks/results.go @@ -99,6 +99,10 @@ type NetworkPage struct { // IsEmpty determines whether or not a NetworkPage is empty. func (page NetworkPage) IsEmpty() (bool, error) { + if page.StatusCode == 204 { + return true, nil + } + va, err := ExtractNetworks(page) return len(va) == 0, err } diff --git a/openstack/compute/v2/extensions/quotasets/results.go b/openstack/compute/v2/extensions/quotasets/results.go index d8df81a63a..07fb49c127 100644 --- a/openstack/compute/v2/extensions/quotasets/results.go +++ b/openstack/compute/v2/extensions/quotasets/results.go @@ -128,6 +128,10 @@ type QuotaSetPage struct { // IsEmpty determines whether or not a QuotaSetsetPage is empty. func (page QuotaSetPage) IsEmpty() (bool, error) { + if page.StatusCode == 204 { + return true, nil + } + ks, err := ExtractQuotaSets(page) return len(ks) == 0, err } diff --git a/openstack/compute/v2/extensions/secgroups/results.go b/openstack/compute/v2/extensions/secgroups/results.go index 0468892206..40c4e94e75 100644 --- a/openstack/compute/v2/extensions/secgroups/results.go +++ b/openstack/compute/v2/extensions/secgroups/results.go @@ -129,6 +129,10 @@ type SecurityGroupPage struct { // IsEmpty determines whether or not a page of Security Groups contains any // results. func (page SecurityGroupPage) IsEmpty() (bool, error) { + if page.StatusCode == 204 { + return true, nil + } + users, err := ExtractSecurityGroups(page) return len(users) == 0, err } diff --git a/openstack/compute/v2/extensions/servergroups/results.go b/openstack/compute/v2/extensions/servergroups/results.go index c8c5b65c81..03eb48d27a 100644 --- a/openstack/compute/v2/extensions/servergroups/results.go +++ b/openstack/compute/v2/extensions/servergroups/results.go @@ -64,6 +64,10 @@ type ServerGroupPage struct { // IsEmpty determines whether or not a ServerGroupsPage is empty. func (page ServerGroupPage) IsEmpty() (bool, error) { + if page.StatusCode == 204 { + return true, nil + } + va, err := ExtractServerGroups(page) return len(va) == 0, err } diff --git a/openstack/compute/v2/extensions/services/results.go b/openstack/compute/v2/extensions/services/results.go index 6c56918fcd..2185b86ec8 100644 --- a/openstack/compute/v2/extensions/services/results.go +++ b/openstack/compute/v2/extensions/services/results.go @@ -98,6 +98,10 @@ type ServicePage struct { // IsEmpty determines whether or not a page of Services contains any results. func (page ServicePage) IsEmpty() (bool, error) { + if page.StatusCode == 204 { + return true, nil + } + services, err := ExtractServices(page) return len(services) == 0, err } diff --git a/openstack/compute/v2/extensions/tenantnetworks/results.go b/openstack/compute/v2/extensions/tenantnetworks/results.go index bda77d5f50..96414fc587 100644 --- a/openstack/compute/v2/extensions/tenantnetworks/results.go +++ b/openstack/compute/v2/extensions/tenantnetworks/results.go @@ -24,6 +24,10 @@ type NetworkPage struct { // IsEmpty determines whether or not a NetworkPage is empty. func (page NetworkPage) IsEmpty() (bool, error) { + if page.StatusCode == 204 { + return true, nil + } + va, err := ExtractNetworks(page) return len(va) == 0, err } diff --git a/openstack/compute/v2/extensions/usage/results.go b/openstack/compute/v2/extensions/usage/results.go index f446730ec2..8c36dde8f2 100644 --- a/openstack/compute/v2/extensions/usage/results.go +++ b/openstack/compute/v2/extensions/usage/results.go @@ -122,6 +122,10 @@ type SingleTenantPage struct { // IsEmpty determines whether or not a SingleTenantPage is empty. func (r SingleTenantPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + ks, err := ExtractSingleTenant(r) return ks == nil, err } @@ -165,6 +169,10 @@ func ExtractAllTenants(page pagination.Page) ([]TenantUsage, error) { // IsEmpty determines whether or not an AllTenantsPage is empty. func (r AllTenantsPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + usages, err := ExtractAllTenants(r) return len(usages) == 0, err } diff --git a/openstack/compute/v2/extensions/volumeattach/results.go b/openstack/compute/v2/extensions/volumeattach/results.go index dec0e87eb5..e5f565a35c 100644 --- a/openstack/compute/v2/extensions/volumeattach/results.go +++ b/openstack/compute/v2/extensions/volumeattach/results.go @@ -37,6 +37,10 @@ type VolumeAttachmentPage struct { // IsEmpty determines whether or not a VolumeAttachmentPage is empty. func (page VolumeAttachmentPage) IsEmpty() (bool, error) { + if page.StatusCode == 204 { + return true, nil + } + va, err := ExtractVolumeAttachments(page) return len(va) == 0, err } diff --git a/openstack/compute/v2/flavors/results.go b/openstack/compute/v2/flavors/results.go index 0234402ed2..4da14118a3 100644 --- a/openstack/compute/v2/flavors/results.go +++ b/openstack/compute/v2/flavors/results.go @@ -121,6 +121,10 @@ type FlavorPage struct { // IsEmpty determines if a FlavorPage contains any results. func (page FlavorPage) IsEmpty() (bool, error) { + if page.StatusCode == 204 { + return true, nil + } + flavors, err := ExtractFlavors(page) return len(flavors) == 0, err } @@ -155,6 +159,10 @@ type AccessPage struct { // IsEmpty indicates whether an AccessPage is empty. func (page AccessPage) IsEmpty() (bool, error) { + if page.StatusCode == 204 { + return true, nil + } + v, err := ExtractAccesses(page) return len(v) == 0, err } diff --git a/openstack/compute/v2/images/results.go b/openstack/compute/v2/images/results.go index 70d1018c72..ca30befeb9 100644 --- a/openstack/compute/v2/images/results.go +++ b/openstack/compute/v2/images/results.go @@ -67,6 +67,10 @@ type ImagePage struct { // IsEmpty returns true if an ImagePage contains no Image results. func (page ImagePage) IsEmpty() (bool, error) { + if page.StatusCode == 204 { + return true, nil + } + images, err := ExtractImages(page) return len(images) == 0, err } diff --git a/openstack/compute/v2/servers/results.go b/openstack/compute/v2/servers/results.go index b92c666783..2c22a3c4d1 100644 --- a/openstack/compute/v2/servers/results.go +++ b/openstack/compute/v2/servers/results.go @@ -277,6 +277,10 @@ type ServerPage struct { // IsEmpty returns true if a page contains no Server results. func (r ServerPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + s, err := ExtractServers(r) return len(s) == 0, err } @@ -385,6 +389,10 @@ type AddressPage struct { // IsEmpty returns true if an AddressPage contains no networks. func (r AddressPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + addresses, err := ExtractAddresses(r) return len(addresses) == 0, err } @@ -410,6 +418,10 @@ type NetworkAddressPage struct { // IsEmpty returns true if a NetworkAddressPage contains no addresses. func (r NetworkAddressPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + addresses, err := ExtractNetworkAddresses(r) return len(addresses) == 0, err } diff --git a/openstack/container/v1/capsules/results.go b/openstack/container/v1/capsules/results.go index ea88af4ce5..9e99c2f7c8 100644 --- a/openstack/container/v1/capsules/results.go +++ b/openstack/container/v1/capsules/results.go @@ -241,6 +241,10 @@ func (r CapsulePage) NextPageURL() (string, error) { // IsEmpty checks whether a CapsulePage struct is empty. func (r CapsulePage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + is, err := ExtractCapsules(r) if err != nil { return false, err diff --git a/openstack/containerinfra/apiversions/results.go b/openstack/containerinfra/apiversions/results.go index b2959802de..01a9795d45 100644 --- a/openstack/containerinfra/apiversions/results.go +++ b/openstack/containerinfra/apiversions/results.go @@ -28,6 +28,10 @@ type APIVersionPage struct { // IsEmpty checks whether an APIVersionPage struct is empty. func (r APIVersionPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + is, err := ExtractAPIVersions(r) return len(is) == 0, err } diff --git a/openstack/containerinfra/v1/clusters/results.go b/openstack/containerinfra/v1/clusters/results.go index 8e8442a940..abfb771850 100644 --- a/openstack/containerinfra/v1/clusters/results.go +++ b/openstack/containerinfra/v1/clusters/results.go @@ -135,6 +135,10 @@ func (r ClusterPage) NextPageURL() (string, error) { // IsEmpty checks whether a ClusterPage struct is empty. func (r ClusterPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + is, err := ExtractClusters(r) return len(is) == 0, err } diff --git a/openstack/containerinfra/v1/clustertemplates/results.go b/openstack/containerinfra/v1/clustertemplates/results.go index 1aa187b90f..552ad2ee44 100644 --- a/openstack/containerinfra/v1/clustertemplates/results.go +++ b/openstack/containerinfra/v1/clustertemplates/results.go @@ -99,6 +99,10 @@ func (r ClusterTemplatePage) NextPageURL() (string, error) { // IsEmpty checks whether a ClusterTemplatePage struct is empty. func (r ClusterTemplatePage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + is, err := ExtractClusterTemplates(r) return len(is) == 0, err } diff --git a/openstack/containerinfra/v1/nodegroups/results.go b/openstack/containerinfra/v1/nodegroups/results.go index 5f3e79f1c6..97efacd058 100644 --- a/openstack/containerinfra/v1/nodegroups/results.go +++ b/openstack/containerinfra/v1/nodegroups/results.go @@ -86,6 +86,10 @@ func (r NodeGroupPage) NextPageURL() (string, error) { } func (r NodeGroupPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + s, err := ExtractNodeGroups(r) return len(s) == 0, err } diff --git a/openstack/db/v1/configurations/results.go b/openstack/db/v1/configurations/results.go index 13bcbffe8b..92006e9170 100644 --- a/openstack/db/v1/configurations/results.go +++ b/openstack/db/v1/configurations/results.go @@ -47,6 +47,10 @@ type ConfigPage struct { // IsEmpty indicates whether a ConfigPage is empty. func (r ConfigPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + is, err := ExtractConfigs(r) return len(is) == 0, err } @@ -114,6 +118,10 @@ type ParamPage struct { // IsEmpty indicates whether a ParamPage is empty. func (r ParamPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + is, err := ExtractParams(r) return len(is) == 0, err } diff --git a/openstack/db/v1/databases/results.go b/openstack/db/v1/databases/results.go index 0479d0e6eb..22c7e2dfe9 100644 --- a/openstack/db/v1/databases/results.go +++ b/openstack/db/v1/databases/results.go @@ -35,6 +35,10 @@ type DBPage struct { // IsEmpty checks to see whether the collection is empty. func (page DBPage) IsEmpty() (bool, error) { + if page.StatusCode == 204 { + return true, nil + } + dbs, err := ExtractDBs(page) return len(dbs) == 0, err } diff --git a/openstack/db/v1/datastores/results.go b/openstack/db/v1/datastores/results.go index a6e27d2745..a3b8be8951 100644 --- a/openstack/db/v1/datastores/results.go +++ b/openstack/db/v1/datastores/results.go @@ -47,6 +47,10 @@ type DatastorePage struct { // IsEmpty indicates whether a Datastore collection is empty. func (r DatastorePage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + is, err := ExtractDatastores(r) return len(is) == 0, err } @@ -77,6 +81,10 @@ type VersionPage struct { // IsEmpty indicates whether a collection of version resources is empty. func (r VersionPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + is, err := ExtractVersions(r) return len(is) == 0, err } diff --git a/openstack/db/v1/flavors/results.go b/openstack/db/v1/flavors/results.go index 0ba515ce3e..716d756ad4 100644 --- a/openstack/db/v1/flavors/results.go +++ b/openstack/db/v1/flavors/results.go @@ -45,6 +45,10 @@ type FlavorPage struct { // IsEmpty determines if a page contains any results. func (page FlavorPage) IsEmpty() (bool, error) { + if page.StatusCode == 204 { + return true, nil + } + flavors, err := ExtractFlavors(page) return len(flavors) == 0, err } diff --git a/openstack/db/v1/instances/results.go b/openstack/db/v1/instances/results.go index 3ac7b02480..b387208ace 100644 --- a/openstack/db/v1/instances/results.go +++ b/openstack/db/v1/instances/results.go @@ -173,6 +173,10 @@ type InstancePage struct { // IsEmpty checks to see whether the collection is empty. func (page InstancePage) IsEmpty() (bool, error) { + if page.StatusCode == 204 { + return true, nil + } + instances, err := ExtractInstances(page) return len(instances) == 0, err } diff --git a/openstack/db/v1/users/results.go b/openstack/db/v1/users/results.go index d12a681bdf..21c1f3ddd1 100644 --- a/openstack/db/v1/users/results.go +++ b/openstack/db/v1/users/results.go @@ -35,6 +35,10 @@ type UserPage struct { // IsEmpty checks to see whether the collection is empty. func (page UserPage) IsEmpty() (bool, error) { + if page.StatusCode == 204 { + return true, nil + } + users, err := ExtractUsers(page) return len(users) == 0, err } diff --git a/openstack/dns/v2/recordsets/results.go b/openstack/dns/v2/recordsets/results.go index 18a0cbd59a..b8c92ccff9 100644 --- a/openstack/dns/v2/recordsets/results.go +++ b/openstack/dns/v2/recordsets/results.go @@ -51,6 +51,10 @@ type DeleteResult struct { // IsEmpty returns true if the page contains no results. func (r RecordSetPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + s, err := ExtractRecordSets(r) return len(s) == 0, err } diff --git a/openstack/dns/v2/transfer/accept/results.go b/openstack/dns/v2/transfer/accept/results.go index 5a820c30a7..85135694b5 100644 --- a/openstack/dns/v2/transfer/accept/results.go +++ b/openstack/dns/v2/transfer/accept/results.go @@ -39,6 +39,10 @@ type TransferAcceptPage struct { // IsEmpty returns true if the page contains no results. func (r TransferAcceptPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + s, err := ExtractTransferAccepts(r) return len(s) == 0, err } diff --git a/openstack/dns/v2/transfer/request/results.go b/openstack/dns/v2/transfer/request/results.go index efb274232f..edbf808985 100644 --- a/openstack/dns/v2/transfer/request/results.go +++ b/openstack/dns/v2/transfer/request/results.go @@ -51,6 +51,10 @@ type TransferRequestPage struct { // IsEmpty returns true if the page contains no results. func (r TransferRequestPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + s, err := ExtractTransferRequests(r) return len(s) == 0, err } diff --git a/openstack/dns/v2/zones/results.go b/openstack/dns/v2/zones/results.go index a36eca7e20..c84c722c08 100644 --- a/openstack/dns/v2/zones/results.go +++ b/openstack/dns/v2/zones/results.go @@ -52,6 +52,10 @@ type ZonePage struct { // IsEmpty returns true if the page contains no results. func (r ZonePage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + s, err := ExtractZones(r) return len(s) == 0, err } diff --git a/openstack/identity/v2/extensions/admin/roles/results.go b/openstack/identity/v2/extensions/admin/roles/results.go index 94eccd6fed..6794d5f697 100644 --- a/openstack/identity/v2/extensions/admin/roles/results.go +++ b/openstack/identity/v2/extensions/admin/roles/results.go @@ -27,6 +27,10 @@ type RolePage struct { // IsEmpty determines whether or not a page of Roles contains any results. func (r RolePage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + users, err := ExtractRoles(r) return len(users) == 0, err } diff --git a/openstack/identity/v2/tenants/results.go b/openstack/identity/v2/tenants/results.go index bb6c2c6b08..2daff98403 100644 --- a/openstack/identity/v2/tenants/results.go +++ b/openstack/identity/v2/tenants/results.go @@ -27,6 +27,10 @@ type TenantPage struct { // 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 } diff --git a/openstack/identity/v2/users/results.go b/openstack/identity/v2/users/results.go index 9f62eee085..4ed1d6ebaf 100644 --- a/openstack/identity/v2/users/results.go +++ b/openstack/identity/v2/users/results.go @@ -48,6 +48,10 @@ type RolePage struct { // IsEmpty determines whether or not a page of Users contains any results. func (r UserPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + users, err := ExtractUsers(r) return len(users) == 0, err } @@ -63,6 +67,10 @@ func ExtractUsers(r pagination.Page) ([]User, error) { // IsEmpty determines whether or not a page of Roles contains any results. func (r RolePage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + users, err := ExtractRoles(r) return len(users) == 0, err } diff --git a/openstack/identity/v3/applicationcredentials/results.go b/openstack/identity/v3/applicationcredentials/results.go index c8d3a42ec8..8ed389d134 100644 --- a/openstack/identity/v3/applicationcredentials/results.go +++ b/openstack/identity/v3/applicationcredentials/results.go @@ -105,6 +105,10 @@ type ApplicationCredentialPage struct { // IsEmpty determines whether or not a an ApplicationCredentialPage contains any results. func (r ApplicationCredentialPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + applicationCredentials, err := ExtractApplicationCredentials(r) return len(applicationCredentials) == 0, err } @@ -155,6 +159,10 @@ type AccessRulePage struct { // IsEmpty determines whether or not a an AccessRulePage contains any results. func (r AccessRulePage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + accessRules, err := ExtractAccessRules(r) return len(accessRules) == 0, err } diff --git a/openstack/identity/v3/catalog/results.go b/openstack/identity/v3/catalog/results.go index c131acd5cf..99dd24c92e 100644 --- a/openstack/identity/v3/catalog/results.go +++ b/openstack/identity/v3/catalog/results.go @@ -12,6 +12,10 @@ type ServiceCatalogPage struct { // IsEmpty returns true if the ServiceCatalogPage contains no results. func (r ServiceCatalogPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + services, err := ExtractServiceCatalog(r) return len(services) == 0, err } diff --git a/openstack/identity/v3/credentials/results.go b/openstack/identity/v3/credentials/results.go index fe4b413e44..312ccf10d7 100644 --- a/openstack/identity/v3/credentials/results.go +++ b/openstack/identity/v3/credentials/results.go @@ -56,6 +56,10 @@ type CredentialPage struct { // IsEmpty determines whether or not a CredentialPage contains any results. func (r CredentialPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + credentials, err := ExtractCredentials(r) return len(credentials) == 0, err } diff --git a/openstack/identity/v3/domains/results.go b/openstack/identity/v3/domains/results.go index 5b8e2662b2..0d927c6db9 100644 --- a/openstack/identity/v3/domains/results.go +++ b/openstack/identity/v3/domains/results.go @@ -58,6 +58,10 @@ type DomainPage struct { // IsEmpty determines whether or not a page of Domains contains any results. func (r DomainPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + domains, err := ExtractDomains(r) return len(domains) == 0, err } diff --git a/openstack/identity/v3/endpoints/results.go b/openstack/identity/v3/endpoints/results.go index 61d201f383..886e1b4987 100644 --- a/openstack/identity/v3/endpoints/results.go +++ b/openstack/identity/v3/endpoints/results.go @@ -69,6 +69,10 @@ type EndpointPage struct { // IsEmpty returns true if no Endpoints were returned. func (r EndpointPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + es, err := ExtractEndpoints(r) return len(es) == 0, err } diff --git a/openstack/identity/v3/extensions/ec2credentials/results.go b/openstack/identity/v3/extensions/ec2credentials/results.go index 829bb1e8f8..bf2d643ecc 100644 --- a/openstack/identity/v3/extensions/ec2credentials/results.go +++ b/openstack/identity/v3/extensions/ec2credentials/results.go @@ -50,6 +50,10 @@ type CredentialPage struct { // IsEmpty determines whether or not a an CredentialPage contains any results. func (r CredentialPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + ec2Credentials, err := ExtractCredentials(r) return len(ec2Credentials) == 0, err } diff --git a/openstack/identity/v3/extensions/federation/results.go b/openstack/identity/v3/extensions/federation/results.go index eeb516ced6..f92cdf23c9 100644 --- a/openstack/identity/v3/extensions/federation/results.go +++ b/openstack/identity/v3/extensions/federation/results.go @@ -175,6 +175,10 @@ type MappingsPage struct { // IsEmpty determines whether or not a page of Mappings contains any results. func (c MappingsPage) IsEmpty() (bool, error) { + if c.StatusCode == 204 { + return true, nil + } + mappings, err := ExtractMappings(c) return len(mappings) == 0, err } diff --git a/openstack/identity/v3/extensions/oauth1/results.go b/openstack/identity/v3/extensions/oauth1/results.go index a67f9381d6..2a37061627 100644 --- a/openstack/identity/v3/extensions/oauth1/results.go +++ b/openstack/identity/v3/extensions/oauth1/results.go @@ -52,6 +52,10 @@ type GetConsumerResult struct { // 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 } @@ -208,6 +212,10 @@ type AccessTokensPage struct { // 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 } @@ -251,6 +259,10 @@ type AccessTokenRolesPage struct { // 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 } diff --git a/openstack/identity/v3/extensions/projectendpoints/results.go b/openstack/identity/v3/extensions/projectendpoints/results.go index 029e58f184..d3c54656aa 100644 --- a/openstack/identity/v3/extensions/projectendpoints/results.go +++ b/openstack/identity/v3/extensions/projectendpoints/results.go @@ -43,6 +43,10 @@ type EndpointPage struct { // IsEmpty returns true if no Endpoints were returned. func (r EndpointPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + es, err := ExtractEndpoints(r) return len(es) == 0, err } diff --git a/openstack/identity/v3/extensions/trusts/results.go b/openstack/identity/v3/extensions/trusts/results.go index 945659cdd8..d1c73d59f1 100644 --- a/openstack/identity/v3/extensions/trusts/results.go +++ b/openstack/identity/v3/extensions/trusts/results.go @@ -36,6 +36,10 @@ type GetResult struct { // IsEmpty determines whether or not a page of Trusts contains any results. func (t TrustPage) IsEmpty() (bool, error) { + if t.StatusCode == 204 { + return true, nil + } + roles, err := ExtractTrusts(t) return len(roles) == 0, err } @@ -109,6 +113,10 @@ type RolesPage struct { // IsEmpty determines whether or not a a Page contains any results. func (r RolesPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + accessTokenRoles, err := ExtractRoles(r) return len(accessTokenRoles) == 0, err } diff --git a/openstack/identity/v3/groups/results.go b/openstack/identity/v3/groups/results.go index f4b0b1a0e7..02bd53a928 100644 --- a/openstack/identity/v3/groups/results.go +++ b/openstack/identity/v3/groups/results.go @@ -93,6 +93,10 @@ type GroupPage struct { // IsEmpty determines whether or not a page of Groups contains any results. func (r GroupPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + groups, err := ExtractGroups(r) return len(groups) == 0, err } diff --git a/openstack/identity/v3/limits/results.go b/openstack/identity/v3/limits/results.go index 16ba63bc77..35867e53cc 100644 --- a/openstack/identity/v3/limits/results.go +++ b/openstack/identity/v3/limits/results.go @@ -66,6 +66,10 @@ type LimitPage struct { // IsEmpty determines whether or not a page of Limits contains any results. func (r LimitPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + limits, err := ExtractLimits(r) return len(limits) == 0, err } diff --git a/openstack/identity/v3/policies/results.go b/openstack/identity/v3/policies/results.go index 6ecf6205bb..fd15f0eacd 100644 --- a/openstack/identity/v3/policies/results.go +++ b/openstack/identity/v3/policies/results.go @@ -91,6 +91,10 @@ type PolicyPage struct { // IsEmpty determines whether or not a page of Policies contains any results. func (r PolicyPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + policies, err := ExtractPolicies(r) return len(policies) == 0, err } diff --git a/openstack/identity/v3/projects/results.go b/openstack/identity/v3/projects/results.go index c2fe821939..8025c8593a 100644 --- a/openstack/identity/v3/projects/results.go +++ b/openstack/identity/v3/projects/results.go @@ -113,6 +113,10 @@ type ProjectPage struct { // IsEmpty determines whether or not a page of Projects contains any results. func (r ProjectPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + projects, err := ExtractProjects(r) return len(projects) == 0, err } diff --git a/openstack/identity/v3/regions/results.go b/openstack/identity/v3/regions/results.go index 6d9050f886..f72ccac09d 100644 --- a/openstack/identity/v3/regions/results.go +++ b/openstack/identity/v3/regions/results.go @@ -90,6 +90,10 @@ type RegionPage struct { // IsEmpty determines whether or not a page of Regions contains any results. func (r RegionPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + regions, err := ExtractRegions(r) return len(regions) == 0, err } diff --git a/openstack/identity/v3/roles/results.go b/openstack/identity/v3/roles/results.go index 1ae032a1e3..9fde609461 100644 --- a/openstack/identity/v3/roles/results.go +++ b/openstack/identity/v3/roles/results.go @@ -90,6 +90,10 @@ type RolePage struct { // IsEmpty determines whether or not a page of Roles contains any results. func (r RolePage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + roles, err := ExtractRoles(r) return len(roles) == 0, err } @@ -182,6 +186,10 @@ type RoleAssignmentPage struct { // IsEmpty returns true if the RoleAssignmentPage contains no results. func (r RoleAssignmentPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + roleAssignments, err := ExtractRoleAssignments(r) return len(roleAssignments) == 0, err } diff --git a/openstack/identity/v3/services/results.go b/openstack/identity/v3/services/results.go index 5ac580ba4b..98bb4449bb 100644 --- a/openstack/identity/v3/services/results.go +++ b/openstack/identity/v3/services/results.go @@ -100,6 +100,10 @@ type ServicePage struct { // IsEmpty returns true if the ServicePage contains no results. func (p ServicePage) IsEmpty() (bool, error) { + if p.StatusCode == 204 { + return true, nil + } + services, err := ExtractServices(p) return len(services) == 0, err } diff --git a/openstack/identity/v3/users/results.go b/openstack/identity/v3/users/results.go index 54a969d5ac..83de3c8bac 100644 --- a/openstack/identity/v3/users/results.go +++ b/openstack/identity/v3/users/results.go @@ -135,6 +135,10 @@ type UserPage struct { // IsEmpty determines whether or not a UserPage contains any results. func (r UserPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + users, err := ExtractUsers(r) return len(users) == 0, err } diff --git a/openstack/imageservice/v2/images/results.go b/openstack/imageservice/v2/images/results.go index d723a466a6..1b27a15495 100644 --- a/openstack/imageservice/v2/images/results.go +++ b/openstack/imageservice/v2/images/results.go @@ -204,6 +204,10 @@ type ImagePage struct { // IsEmpty returns true if an ImagePage contains no Images results. func (r ImagePage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + images, err := ExtractImages(r) return len(images) == 0, err } diff --git a/openstack/imageservice/v2/members/results.go b/openstack/imageservice/v2/members/results.go index ab694bdc0f..8996635b6d 100644 --- a/openstack/imageservice/v2/members/results.go +++ b/openstack/imageservice/v2/members/results.go @@ -41,6 +41,10 @@ func ExtractMembers(r pagination.Page) ([]Member, error) { // IsEmpty determines whether or not a MemberPage contains any results. func (r MemberPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + members, err := ExtractMembers(r) return len(members) == 0, err } diff --git a/openstack/imageservice/v2/tasks/results.go b/openstack/imageservice/v2/tasks/results.go index 3a7f5ca12b..04df85928a 100644 --- a/openstack/imageservice/v2/tasks/results.go +++ b/openstack/imageservice/v2/tasks/results.go @@ -81,6 +81,10 @@ type TaskPage struct { // IsEmpty returns true if a TaskPage contains no Tasks results. func (r TaskPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + tasks, err := ExtractTasks(r) return len(tasks) == 0, err } diff --git a/openstack/keymanager/v1/containers/results.go b/openstack/keymanager/v1/containers/results.go index ce7e28f786..c89a960958 100644 --- a/openstack/keymanager/v1/containers/results.go +++ b/openstack/keymanager/v1/containers/results.go @@ -108,6 +108,10 @@ type ContainerPage struct { // IsEmpty determines whether or not a page of Container contains any results. func (r ContainerPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + containers, err := ExtractContainers(r) return len(containers) == 0, err } @@ -204,6 +208,10 @@ type ConsumerPage struct { // IsEmpty determines whether or not a page of consumers contains any results. func (r ConsumerPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + consumers, err := ExtractConsumers(r) return len(consumers) == 0, err } diff --git a/openstack/keymanager/v1/orders/results.go b/openstack/keymanager/v1/orders/results.go index 35e89b50ba..fa67c12dda 100644 --- a/openstack/keymanager/v1/orders/results.go +++ b/openstack/keymanager/v1/orders/results.go @@ -135,6 +135,10 @@ type OrderPage struct { // IsEmpty determines whether or not a page of ordersS contains any results. func (r OrderPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + orders, err := ExtractOrders(r) return len(orders) == 0, err } diff --git a/openstack/keymanager/v1/secrets/results.go b/openstack/keymanager/v1/secrets/results.go index bd36036f3a..f76b977520 100644 --- a/openstack/keymanager/v1/secrets/results.go +++ b/openstack/keymanager/v1/secrets/results.go @@ -136,6 +136,10 @@ type SecretPage struct { // IsEmpty determines whether or not a page of secrets contains any results. func (r SecretPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + secrets, err := ExtractSecrets(r) return len(secrets) == 0, err } diff --git a/openstack/loadbalancer/v2/amphorae/results.go b/openstack/loadbalancer/v2/amphorae/results.go index 2082a8f40d..edbddc63b4 100644 --- a/openstack/loadbalancer/v2/amphorae/results.go +++ b/openstack/loadbalancer/v2/amphorae/results.go @@ -113,6 +113,10 @@ func (r AmphoraPage) NextPageURL() (string, error) { // IsEmpty checks whether a AmphoraPage struct is empty. func (r AmphoraPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + is, err := ExtractAmphorae(r) return len(is) == 0, err } diff --git a/openstack/loadbalancer/v2/apiversions/results.go b/openstack/loadbalancer/v2/apiversions/results.go index b031cb8236..dce4aa5d14 100644 --- a/openstack/loadbalancer/v2/apiversions/results.go +++ b/openstack/loadbalancer/v2/apiversions/results.go @@ -17,6 +17,10 @@ type APIVersionPage struct { // IsEmpty checks whether an APIVersionPage struct is empty. func (r APIVersionPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + is, err := ExtractAPIVersions(r) return len(is) == 0, err } diff --git a/openstack/loadbalancer/v2/l7policies/results.go b/openstack/loadbalancer/v2/l7policies/results.go index cf236d742c..bc5b32139a 100644 --- a/openstack/loadbalancer/v2/l7policies/results.go +++ b/openstack/loadbalancer/v2/l7policies/results.go @@ -137,6 +137,10 @@ func (r L7PolicyPage) NextPageURL() (string, error) { // IsEmpty checks whether a L7PolicyPage struct is empty. func (r L7PolicyPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + is, err := ExtractL7Policies(r) return len(is) == 0, err } @@ -211,6 +215,10 @@ func (r RulePage) NextPageURL() (string, error) { // IsEmpty checks whether a RulePage struct is empty. func (r RulePage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + is, err := ExtractRules(r) return len(is) == 0, err } diff --git a/openstack/loadbalancer/v2/listeners/results.go b/openstack/loadbalancer/v2/listeners/results.go index 1a953fc88b..234b6cb062 100644 --- a/openstack/loadbalancer/v2/listeners/results.go +++ b/openstack/loadbalancer/v2/listeners/results.go @@ -157,6 +157,10 @@ func (r ListenerPage) NextPageURL() (string, error) { // IsEmpty checks whether a ListenerPage struct is empty. func (r ListenerPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + is, err := ExtractListeners(r) return len(is) == 0, err } diff --git a/openstack/loadbalancer/v2/loadbalancers/results.go b/openstack/loadbalancer/v2/loadbalancers/results.go index 739337c4d6..71f750dd6a 100644 --- a/openstack/loadbalancer/v2/loadbalancers/results.go +++ b/openstack/loadbalancer/v2/loadbalancers/results.go @@ -161,6 +161,10 @@ func (r LoadBalancerPage) NextPageURL() (string, error) { // IsEmpty checks whether a LoadBalancerPage struct is empty. func (r LoadBalancerPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + is, err := ExtractLoadBalancers(r) return len(is) == 0, err } diff --git a/openstack/loadbalancer/v2/monitors/results.go b/openstack/loadbalancer/v2/monitors/results.go index 27d61fd466..502581feba 100644 --- a/openstack/loadbalancer/v2/monitors/results.go +++ b/openstack/loadbalancer/v2/monitors/results.go @@ -110,6 +110,10 @@ func (r MonitorPage) NextPageURL() (string, error) { // IsEmpty checks whether a MonitorPage struct is empty. func (r MonitorPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + is, err := ExtractMonitors(r) return len(is) == 0, err } diff --git a/openstack/loadbalancer/v2/pools/results.go b/openstack/loadbalancer/v2/pools/results.go index b1466879b3..44379b6f38 100644 --- a/openstack/loadbalancer/v2/pools/results.go +++ b/openstack/loadbalancer/v2/pools/results.go @@ -135,6 +135,10 @@ func (r PoolPage) NextPageURL() (string, error) { // IsEmpty checks whether a PoolPage struct is empty. func (r PoolPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + is, err := ExtractPools(r) return len(is) == 0, err } @@ -265,6 +269,10 @@ func (r MemberPage) NextPageURL() (string, error) { // IsEmpty checks whether a MemberPage struct is empty. func (r MemberPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + is, err := ExtractMembers(r) return len(is) == 0, err } diff --git a/openstack/loadbalancer/v2/providers/results.go b/openstack/loadbalancer/v2/providers/results.go index 7a7d8afcee..509f815fed 100644 --- a/openstack/loadbalancer/v2/providers/results.go +++ b/openstack/loadbalancer/v2/providers/results.go @@ -36,6 +36,10 @@ func (r ProviderPage) NextPageURL() (string, error) { // IsEmpty checks whether a ProviderPage struct is empty. func (r ProviderPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + is, err := ExtractProviders(r) return len(is) == 0, err } diff --git a/openstack/messaging/v2/messages/results.go b/openstack/messaging/v2/messages/results.go index 1c361e3f6d..ed1208b3f3 100644 --- a/openstack/messaging/v2/messages/results.go +++ b/openstack/messaging/v2/messages/results.go @@ -109,6 +109,10 @@ func ExtractMessages(r pagination.Page) ([]Message, error) { // IsEmpty determines if a MessagePage contains any results. func (r MessagePage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + s, err := ExtractMessages(r) return len(s) == 0, err } diff --git a/openstack/messaging/v2/queues/results.go b/openstack/messaging/v2/queues/results.go index 92d2fc67c1..45a9083957 100644 --- a/openstack/messaging/v2/queues/results.go +++ b/openstack/messaging/v2/queues/results.go @@ -150,6 +150,10 @@ func ExtractQueues(r pagination.Page) ([]Queue, error) { // IsEmpty determines if a QueuesPage contains any results. func (r QueuePage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + s, err := ExtractQueues(r) return len(s) == 0, err } diff --git a/openstack/networking/v2/apiversions/results.go b/openstack/networking/v2/apiversions/results.go index eff44855d7..ad9d092fa0 100644 --- a/openstack/networking/v2/apiversions/results.go +++ b/openstack/networking/v2/apiversions/results.go @@ -19,6 +19,10 @@ type APIVersionPage struct { // IsEmpty checks whether an APIVersionPage struct is empty. func (r APIVersionPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + is, err := ExtractAPIVersions(r) return len(is) == 0, err } @@ -49,6 +53,10 @@ type APIVersionResourcePage struct { // IsEmpty is a concrete function which indicates whether an // APIVersionResourcePage is empty or not. func (r APIVersionResourcePage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + is, err := ExtractVersionResources(r) return len(is) == 0, err } diff --git a/openstack/networking/v2/extensions/agents/results.go b/openstack/networking/v2/extensions/agents/results.go index 0af9e0fdd0..01af98c4e2 100644 --- a/openstack/networking/v2/extensions/agents/results.go +++ b/openstack/networking/v2/extensions/agents/results.go @@ -160,6 +160,10 @@ func (r AgentPage) NextPageURL() (string, error) { // IsEmpty determines whether or not a AgentPage is empty. func (r AgentPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + agents, err := ExtractAgents(r) return len(agents) == 0, err } @@ -196,6 +200,10 @@ type ListBGPSpeakersResult struct { } func (r ListBGPSpeakersResult) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + speakers, err := ExtractBGPSpeakers(r) return 0 == len(speakers), err } diff --git a/openstack/networking/v2/extensions/bgp/peers/results.go b/openstack/networking/v2/extensions/bgp/peers/results.go index 0f87f53f7d..c20ed238ff 100644 --- a/openstack/networking/v2/extensions/bgp/peers/results.go +++ b/openstack/networking/v2/extensions/bgp/peers/results.go @@ -54,6 +54,10 @@ type BGPPeerPage struct { // IsEmpty checks whether a BGPPage struct is empty. func (r BGPPeerPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + is, err := ExtractBGPPeers(r) return len(is) == 0, err } diff --git a/openstack/networking/v2/extensions/bgp/speakers/results.go b/openstack/networking/v2/extensions/bgp/speakers/results.go index 6d0f444620..3f8377f399 100644 --- a/openstack/networking/v2/extensions/bgp/speakers/results.go +++ b/openstack/networking/v2/extensions/bgp/speakers/results.go @@ -63,6 +63,10 @@ type BGPSpeakerPage struct { // IsEmpty checks whether a BGPSpeakerPage struct is empty. func (r BGPSpeakerPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + is, err := ExtractBGPSpeakers(r) return len(is) == 0, err } @@ -145,6 +149,10 @@ type AdvertisedRoutePage struct { // IsEmpty checks whether a AdvertisedRoutePage struct is empty. func (r AdvertisedRoutePage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + is, err := ExtractAdvertisedRoutes(r) return len(is) == 0, err } diff --git a/openstack/networking/v2/extensions/fwaas/firewalls/results.go b/openstack/networking/v2/extensions/fwaas/firewalls/results.go index 9543f0fae4..edefdd6fe8 100644 --- a/openstack/networking/v2/extensions/fwaas/firewalls/results.go +++ b/openstack/networking/v2/extensions/fwaas/firewalls/results.go @@ -58,6 +58,10 @@ func (r FirewallPage) NextPageURL() (string, error) { // IsEmpty checks whether a FirewallPage struct is empty. func (r FirewallPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + is, err := ExtractFirewalls(r) return len(is) == 0, err } diff --git a/openstack/networking/v2/extensions/fwaas/policies/results.go b/openstack/networking/v2/extensions/fwaas/policies/results.go index 495cef2c0e..6796f0ceb8 100644 --- a/openstack/networking/v2/extensions/fwaas/policies/results.go +++ b/openstack/networking/v2/extensions/fwaas/policies/results.go @@ -52,6 +52,10 @@ func (r PolicyPage) NextPageURL() (string, error) { // IsEmpty checks whether a PolicyPage struct is empty. func (r PolicyPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + is, err := ExtractPolicies(r) return len(is) == 0, err } diff --git a/openstack/networking/v2/extensions/fwaas/rules/results.go b/openstack/networking/v2/extensions/fwaas/rules/results.go index 82bf4a36a8..f7b44a05fc 100644 --- a/openstack/networking/v2/extensions/fwaas/rules/results.go +++ b/openstack/networking/v2/extensions/fwaas/rules/results.go @@ -47,6 +47,10 @@ func (r RulePage) NextPageURL() (string, error) { // IsEmpty checks whether a RulePage struct is empty. func (r RulePage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + is, err := ExtractRules(r) return len(is) == 0, err } diff --git a/openstack/networking/v2/extensions/fwaas_v2/groups/results.go b/openstack/networking/v2/extensions/fwaas_v2/groups/results.go index c0af2b0a9d..793a5d4328 100644 --- a/openstack/networking/v2/extensions/fwaas_v2/groups/results.go +++ b/openstack/networking/v2/extensions/fwaas_v2/groups/results.go @@ -55,6 +55,10 @@ func (r GroupPage) NextPageURL() (string, error) { // IsEmpty checks whether a GroupPage struct is empty. func (r GroupPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + is, err := ExtractGroups(r) return len(is) == 0, err } diff --git a/openstack/networking/v2/extensions/fwaas_v2/policies/results.go b/openstack/networking/v2/extensions/fwaas_v2/policies/results.go index a3c212eb6f..c0139aea3f 100644 --- a/openstack/networking/v2/extensions/fwaas_v2/policies/results.go +++ b/openstack/networking/v2/extensions/fwaas_v2/policies/results.go @@ -62,6 +62,10 @@ func (r PolicyPage) NextPageURL() (string, error) { // IsEmpty checks whether a PolicyPage struct is empty. func (r PolicyPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + is, err := ExtractPolicies(r) return len(is) == 0, err } diff --git a/openstack/networking/v2/extensions/fwaas_v2/rules/results.go b/openstack/networking/v2/extensions/fwaas_v2/rules/results.go index ab51ef9f47..1d085b1459 100644 --- a/openstack/networking/v2/extensions/fwaas_v2/rules/results.go +++ b/openstack/networking/v2/extensions/fwaas_v2/rules/results.go @@ -46,6 +46,10 @@ func (r RulePage) NextPageURL() (string, error) { // IsEmpty checks whether a RulePage struct is empty. func (r RulePage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + is, err := ExtractRules(r) return len(is) == 0, err } diff --git a/openstack/networking/v2/extensions/layer3/addressscopes/results.go b/openstack/networking/v2/extensions/layer3/addressscopes/results.go index 5f78dfeef2..8a94aaaef5 100644 --- a/openstack/networking/v2/extensions/layer3/addressscopes/results.go +++ b/openstack/networking/v2/extensions/layer3/addressscopes/results.go @@ -84,6 +84,10 @@ func (r AddressScopePage) NextPageURL() (string, error) { // IsEmpty determines whether or not a AddressScopePage is empty. func (r AddressScopePage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + addressScopes, err := ExtractAddressScopes(r) return len(addressScopes) == 0, err } diff --git a/openstack/networking/v2/extensions/layer3/floatingips/results.go b/openstack/networking/v2/extensions/layer3/floatingips/results.go index 0d287dffaa..c4c019bca4 100644 --- a/openstack/networking/v2/extensions/layer3/floatingips/results.go +++ b/openstack/networking/v2/extensions/layer3/floatingips/results.go @@ -157,6 +157,10 @@ func (r FloatingIPPage) NextPageURL() (string, error) { // IsEmpty checks whether a FloatingIPPage struct is empty. func (r FloatingIPPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + is, err := ExtractFloatingIPs(r) return len(is) == 0, err } diff --git a/openstack/networking/v2/extensions/layer3/portforwarding/results.go b/openstack/networking/v2/extensions/layer3/portforwarding/results.go index 3aa1f3eb6b..70bd097336 100644 --- a/openstack/networking/v2/extensions/layer3/portforwarding/results.go +++ b/openstack/networking/v2/extensions/layer3/portforwarding/results.go @@ -88,6 +88,10 @@ func (r PortForwardingPage) NextPageURL() (string, error) { // IsEmpty checks whether a PortForwardingPage struct is empty. func (r PortForwardingPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + is, err := ExtractPortForwardings(r) return len(is) == 0, err } diff --git a/openstack/networking/v2/extensions/layer3/routers/results.go b/openstack/networking/v2/extensions/layer3/routers/results.go index a6c93cfd0a..0c93918991 100644 --- a/openstack/networking/v2/extensions/layer3/routers/results.go +++ b/openstack/networking/v2/extensions/layer3/routers/results.go @@ -100,6 +100,10 @@ func (r RouterPage) NextPageURL() (string, error) { // IsEmpty checks whether a RouterPage struct is empty. func (r RouterPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + is, err := ExtractRouters(r) return len(is) == 0, err } @@ -263,6 +267,10 @@ type ListL3AgentsPage struct { } func (r ListL3AgentsPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + v, err := ExtractL3Agents(r) return len(v) == 0, err } diff --git a/openstack/networking/v2/extensions/lbaas/members/results.go b/openstack/networking/v2/extensions/lbaas/members/results.go index 804dbe8445..c23308def7 100644 --- a/openstack/networking/v2/extensions/lbaas/members/results.go +++ b/openstack/networking/v2/extensions/lbaas/members/results.go @@ -56,6 +56,10 @@ func (r MemberPage) NextPageURL() (string, error) { // IsEmpty checks whether a MemberPage struct is empty. func (r MemberPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + is, err := ExtractMembers(r) return len(is) == 0, err } diff --git a/openstack/networking/v2/extensions/lbaas/monitors/results.go b/openstack/networking/v2/extensions/lbaas/monitors/results.go index cc99f7cced..cf0dd9a75c 100644 --- a/openstack/networking/v2/extensions/lbaas/monitors/results.go +++ b/openstack/networking/v2/extensions/lbaas/monitors/results.go @@ -88,6 +88,10 @@ func (r MonitorPage) NextPageURL() (string, error) { // IsEmpty checks whether a PoolPage struct is empty. func (r MonitorPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + is, err := ExtractMonitors(r) return len(is) == 0, err } diff --git a/openstack/networking/v2/extensions/lbaas/pools/results.go b/openstack/networking/v2/extensions/lbaas/pools/results.go index c2bae82d56..d94c6b0b5a 100644 --- a/openstack/networking/v2/extensions/lbaas/pools/results.go +++ b/openstack/networking/v2/extensions/lbaas/pools/results.go @@ -78,6 +78,10 @@ func (r PoolPage) NextPageURL() (string, error) { // IsEmpty checks whether a PoolPage struct is empty. func (r PoolPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + is, err := ExtractPools(r) return len(is) == 0, err } diff --git a/openstack/networking/v2/extensions/lbaas/vips/results.go b/openstack/networking/v2/extensions/lbaas/vips/results.go index 1351e352bb..a6a5bb5836 100644 --- a/openstack/networking/v2/extensions/lbaas/vips/results.go +++ b/openstack/networking/v2/extensions/lbaas/vips/results.go @@ -108,6 +108,10 @@ func (r VIPPage) NextPageURL() (string, error) { // IsEmpty checks whether a VIPPage struct is empty. func (r VIPPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + is, err := ExtractVIPs(r) return len(is) == 0, err } diff --git a/openstack/networking/v2/extensions/lbaas_v2/l7policies/results.go b/openstack/networking/v2/extensions/lbaas_v2/l7policies/results.go index 5153b1b90c..5123d85caf 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/l7policies/results.go +++ b/openstack/networking/v2/extensions/lbaas_v2/l7policies/results.go @@ -137,6 +137,10 @@ func (r L7PolicyPage) NextPageURL() (string, error) { // IsEmpty checks whether a L7PolicyPage struct is empty. func (r L7PolicyPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + is, err := ExtractL7Policies(r) return len(is) == 0, err } @@ -211,6 +215,10 @@ func (r RulePage) NextPageURL() (string, error) { // IsEmpty checks whether a RulePage struct is empty. func (r RulePage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + is, err := ExtractRules(r) return len(is) == 0, err } diff --git a/openstack/networking/v2/extensions/lbaas_v2/listeners/results.go b/openstack/networking/v2/extensions/lbaas_v2/listeners/results.go index ae10579322..6bf13cf68d 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/listeners/results.go +++ b/openstack/networking/v2/extensions/lbaas_v2/listeners/results.go @@ -88,6 +88,10 @@ func (r ListenerPage) NextPageURL() (string, error) { // IsEmpty checks whether a ListenerPage struct is empty. func (r ListenerPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + is, err := ExtractListeners(r) return len(is) == 0, err } diff --git a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/results.go b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/results.go index 286f0b7c11..a9027fb7fa 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/results.go +++ b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/results.go @@ -101,6 +101,10 @@ func (r LoadBalancerPage) NextPageURL() (string, error) { // IsEmpty checks whether a LoadBalancerPage struct is empty. func (r LoadBalancerPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + is, err := ExtractLoadBalancers(r) return len(is) == 0, err } diff --git a/openstack/networking/v2/extensions/lbaas_v2/monitors/results.go b/openstack/networking/v2/extensions/lbaas_v2/monitors/results.go index a78f7aeb0f..ee96038f3c 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/monitors/results.go +++ b/openstack/networking/v2/extensions/lbaas_v2/monitors/results.go @@ -100,6 +100,10 @@ func (r MonitorPage) NextPageURL() (string, error) { // IsEmpty checks whether a MonitorPage struct is empty. func (r MonitorPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + is, err := ExtractMonitors(r) return len(is) == 0, err } diff --git a/openstack/networking/v2/extensions/lbaas_v2/pools/results.go b/openstack/networking/v2/extensions/lbaas_v2/pools/results.go index 761264e48e..c747f93a71 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/pools/results.go +++ b/openstack/networking/v2/extensions/lbaas_v2/pools/results.go @@ -130,6 +130,10 @@ func (r PoolPage) NextPageURL() (string, error) { // IsEmpty checks whether a PoolPage struct is empty. func (r PoolPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + is, err := ExtractPools(r) return len(is) == 0, err } @@ -243,6 +247,10 @@ func (r MemberPage) NextPageURL() (string, error) { // IsEmpty checks whether a MemberPage struct is empty. func (r MemberPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + is, err := ExtractMembers(r) return len(is) == 0, err } diff --git a/openstack/networking/v2/extensions/networkipavailabilities/results.go b/openstack/networking/v2/extensions/networkipavailabilities/results.go index db62b73ca4..c3bd7e1cdb 100644 --- a/openstack/networking/v2/extensions/networkipavailabilities/results.go +++ b/openstack/networking/v2/extensions/networkipavailabilities/results.go @@ -121,6 +121,10 @@ type NetworkIPAvailabilityPage struct { // IsEmpty determines whether or not a NetworkIPAvailability is empty. func (r NetworkIPAvailabilityPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + networkipavailabilities, err := ExtractNetworkIPAvailabilities(r) return len(networkipavailabilities) == 0, err } diff --git a/openstack/networking/v2/extensions/qos/policies/results.go b/openstack/networking/v2/extensions/qos/policies/results.go index 4378181335..356f116a00 100644 --- a/openstack/networking/v2/extensions/qos/policies/results.go +++ b/openstack/networking/v2/extensions/qos/policies/results.go @@ -110,6 +110,10 @@ func (r PolicyPage) NextPageURL() (string, error) { // IsEmpty checks whether a PolicyPage is empty. func (r PolicyPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + is, err := ExtractPolicies(r) return len(is) == 0, err } diff --git a/openstack/networking/v2/extensions/qos/rules/results.go b/openstack/networking/v2/extensions/qos/rules/results.go index ec193465b7..7a8a08588a 100644 --- a/openstack/networking/v2/extensions/qos/rules/results.go +++ b/openstack/networking/v2/extensions/qos/rules/results.go @@ -70,6 +70,10 @@ type BandwidthLimitRulePage struct { // IsEmpty checks whether a BandwidthLimitRulePage is empty. func (r BandwidthLimitRulePage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + is, err := ExtractBandwidthLimitRules(r) return len(is) == 0, err } @@ -142,6 +146,10 @@ type DSCPMarkingRulePage struct { // IsEmpty checks whether a DSCPMarkingRulePage is empty. func (r DSCPMarkingRulePage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + is, err := ExtractDSCPMarkingRules(r) return len(is) == 0, err } @@ -217,6 +225,10 @@ type MinimumBandwidthRulePage struct { // IsEmpty checks whether a MinimumBandwidthRulePage is empty. func (r MinimumBandwidthRulePage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + is, err := ExtractMinimumBandwidthRules(r) return len(is) == 0, err } diff --git a/openstack/networking/v2/extensions/qos/ruletypes/results.go b/openstack/networking/v2/extensions/qos/ruletypes/results.go index a5cfe9d0be..c237f347c1 100644 --- a/openstack/networking/v2/extensions/qos/ruletypes/results.go +++ b/openstack/networking/v2/extensions/qos/ruletypes/results.go @@ -47,6 +47,10 @@ type ListRuleTypesPage struct { } func (r ListRuleTypesPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + v, err := ExtractRuleTypes(r) return len(v) == 0, err } diff --git a/openstack/networking/v2/extensions/rbacpolicies/results.go b/openstack/networking/v2/extensions/rbacpolicies/results.go index 1327b17b99..53ef46c590 100644 --- a/openstack/networking/v2/extensions/rbacpolicies/results.go +++ b/openstack/networking/v2/extensions/rbacpolicies/results.go @@ -82,6 +82,10 @@ type RBACPolicyPage struct { // IsEmpty checks whether a RBACPolicyPage struct is empty. func (r RBACPolicyPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + is, err := ExtractRBACPolicies(r) return len(is) == 0, err } diff --git a/openstack/networking/v2/extensions/security/groups/results.go b/openstack/networking/v2/extensions/security/groups/results.go index 960862bb38..2027037de2 100644 --- a/openstack/networking/v2/extensions/security/groups/results.go +++ b/openstack/networking/v2/extensions/security/groups/results.go @@ -101,6 +101,10 @@ func (r SecGroupPage) NextPageURL() (string, error) { // IsEmpty checks whether a SecGroupPage struct is empty. func (r SecGroupPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + is, err := ExtractGroups(r) return len(is) == 0, err } diff --git a/openstack/networking/v2/extensions/security/rules/results.go b/openstack/networking/v2/extensions/security/rules/results.go index 52ac3f7a75..1e65f0627c 100644 --- a/openstack/networking/v2/extensions/security/rules/results.go +++ b/openstack/networking/v2/extensions/security/rules/results.go @@ -80,6 +80,10 @@ func (r SecGroupRulePage) NextPageURL() (string, error) { // IsEmpty checks whether a SecGroupRulePage struct is empty. func (r SecGroupRulePage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + is, err := ExtractRules(r) return len(is) == 0, err } diff --git a/openstack/networking/v2/extensions/subnetpools/results.go b/openstack/networking/v2/extensions/subnetpools/results.go index eadf005781..3fb879dfa5 100644 --- a/openstack/networking/v2/extensions/subnetpools/results.go +++ b/openstack/networking/v2/extensions/subnetpools/results.go @@ -251,6 +251,10 @@ func (r SubnetPoolPage) NextPageURL() (string, error) { // IsEmpty determines whether or not a SubnetPoolPage is empty. func (r SubnetPoolPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + subnetpools, err := ExtractSubnetPools(r) return len(subnetpools) == 0, err } diff --git a/openstack/networking/v2/extensions/trunks/results.go b/openstack/networking/v2/extensions/trunks/results.go index 6d979ef7a2..6b66276032 100644 --- a/openstack/networking/v2/extensions/trunks/results.go +++ b/openstack/networking/v2/extensions/trunks/results.go @@ -111,6 +111,10 @@ type TrunkPage struct { } func (page TrunkPage) IsEmpty() (bool, error) { + if page.StatusCode == 204 { + return true, nil + } + trunks, err := ExtractTrunks(page) return len(trunks) == 0, err } diff --git a/openstack/networking/v2/extensions/vpnaas/endpointgroups/results.go b/openstack/networking/v2/extensions/vpnaas/endpointgroups/results.go index 822b70002c..e8cfd2510b 100644 --- a/openstack/networking/v2/extensions/vpnaas/endpointgroups/results.go +++ b/openstack/networking/v2/extensions/vpnaas/endpointgroups/results.go @@ -64,6 +64,10 @@ func (r EndpointGroupPage) NextPageURL() (string, error) { // IsEmpty checks whether an EndpointGroupPage struct is empty. func (r EndpointGroupPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + is, err := ExtractEndpointGroups(r) return len(is) == 0, err } diff --git a/openstack/networking/v2/extensions/vpnaas/ikepolicies/results.go b/openstack/networking/v2/extensions/vpnaas/ikepolicies/results.go index b825f5754f..e5d33a4db1 100644 --- a/openstack/networking/v2/extensions/vpnaas/ikepolicies/results.go +++ b/openstack/networking/v2/extensions/vpnaas/ikepolicies/results.go @@ -85,6 +85,10 @@ func (r PolicyPage) NextPageURL() (string, error) { // IsEmpty checks whether a PolicyPage struct is empty. func (r PolicyPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + is, err := ExtractPolicies(r) return len(is) == 0, err } diff --git a/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/results.go b/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/results.go index eda4a1bd23..1268ec61b6 100644 --- a/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/results.go +++ b/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/results.go @@ -104,6 +104,10 @@ func (r PolicyPage) NextPageURL() (string, error) { // IsEmpty checks whether a PolicyPage struct is empty. func (r PolicyPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + is, err := ExtractPolicies(r) return len(is) == 0, err } diff --git a/openstack/networking/v2/extensions/vpnaas/services/results.go b/openstack/networking/v2/extensions/vpnaas/services/results.go index 5e555699fc..c3aa9698c1 100644 --- a/openstack/networking/v2/extensions/vpnaas/services/results.go +++ b/openstack/networking/v2/extensions/vpnaas/services/results.go @@ -72,6 +72,10 @@ func (r ServicePage) NextPageURL() (string, error) { // IsEmpty checks whether a ServicePage struct is empty. func (r ServicePage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + is, err := ExtractServices(r) return len(is) == 0, err } diff --git a/openstack/networking/v2/extensions/vpnaas/siteconnections/results.go b/openstack/networking/v2/extensions/vpnaas/siteconnections/results.go index 3c09e4d074..d0fb6403b2 100644 --- a/openstack/networking/v2/extensions/vpnaas/siteconnections/results.go +++ b/openstack/networking/v2/extensions/vpnaas/siteconnections/results.go @@ -114,6 +114,10 @@ func (r ConnectionPage) NextPageURL() (string, error) { // IsEmpty checks whether a ConnectionPage struct is empty. func (r ConnectionPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + is, err := ExtractConnections(r) return len(is) == 0, err } diff --git a/openstack/networking/v2/networks/results.go b/openstack/networking/v2/networks/results.go index 4e175ce557..2a020a13f0 100644 --- a/openstack/networking/v2/networks/results.go +++ b/openstack/networking/v2/networks/results.go @@ -155,6 +155,10 @@ func (r NetworkPage) NextPageURL() (string, error) { // IsEmpty checks whether a NetworkPage struct is empty. func (r NetworkPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + is, err := ExtractNetworks(r) return len(is) == 0, err } diff --git a/openstack/networking/v2/ports/results.go b/openstack/networking/v2/ports/results.go index cf580bbc19..a39133fc06 100644 --- a/openstack/networking/v2/ports/results.go +++ b/openstack/networking/v2/ports/results.go @@ -187,6 +187,10 @@ func (r PortPage) NextPageURL() (string, error) { // IsEmpty checks whether a PortPage struct is empty. func (r PortPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + is, err := ExtractPorts(r) return len(is) == 0, err } diff --git a/openstack/networking/v2/subnets/results.go b/openstack/networking/v2/subnets/results.go index 63b98f7248..cd09b6f6e6 100644 --- a/openstack/networking/v2/subnets/results.go +++ b/openstack/networking/v2/subnets/results.go @@ -142,6 +142,10 @@ func (r SubnetPage) NextPageURL() (string, error) { // IsEmpty checks whether a SubnetPage struct is empty. func (r SubnetPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + is, err := ExtractSubnets(r) return len(is) == 0, err } diff --git a/openstack/orchestration/v1/apiversions/results.go b/openstack/orchestration/v1/apiversions/results.go index a7c22a2739..a14f7ebaab 100644 --- a/openstack/orchestration/v1/apiversions/results.go +++ b/openstack/orchestration/v1/apiversions/results.go @@ -21,6 +21,10 @@ type APIVersionPage struct { // IsEmpty checks whether an APIVersionPage struct is empty. func (r APIVersionPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + is, err := ExtractAPIVersions(r) return len(is) == 0, err } diff --git a/openstack/orchestration/v1/stackevents/results.go b/openstack/orchestration/v1/stackevents/results.go index 75f7d3f386..afe81b3771 100644 --- a/openstack/orchestration/v1/stackevents/results.go +++ b/openstack/orchestration/v1/stackevents/results.go @@ -82,6 +82,10 @@ type EventPage struct { // IsEmpty returns true if a page contains no Server results. func (r EventPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + events, err := ExtractEvents(r) return len(events) == 0, err } diff --git a/openstack/orchestration/v1/stackresources/results.go b/openstack/orchestration/v1/stackresources/results.go index 8b9495839a..8de3fba1a0 100644 --- a/openstack/orchestration/v1/stackresources/results.go +++ b/openstack/orchestration/v1/stackresources/results.go @@ -89,6 +89,10 @@ type ResourcePage struct { // IsEmpty returns true if a page contains no Server results. func (r ResourcePage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + resources, err := ExtractResources(r) return len(resources) == 0, err } @@ -141,6 +145,10 @@ type ResourceTypePage struct { // IsEmpty returns true if a ResourceTypePage contains no resource types. func (r ResourceTypePage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + rts, err := ExtractResourceTypes(r) return len(rts) == 0, err } diff --git a/openstack/orchestration/v1/stacks/results.go b/openstack/orchestration/v1/stacks/results.go index 054ab3d74b..a741164b6f 100644 --- a/openstack/orchestration/v1/stacks/results.go +++ b/openstack/orchestration/v1/stacks/results.go @@ -42,6 +42,10 @@ type StackPage struct { // IsEmpty returns true if a ListResult contains no Stacks. func (r StackPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + stacks, err := ExtractStacks(r) return len(stacks) == 0, err } diff --git a/openstack/placement/v1/resourceproviders/results.go b/openstack/placement/v1/resourceproviders/results.go index cafff8b0ac..225f18ef8c 100644 --- a/openstack/placement/v1/resourceproviders/results.go +++ b/openstack/placement/v1/resourceproviders/results.go @@ -110,6 +110,10 @@ type ResourceProvidersPage struct { // IsEmpty determines if a ResourceProvidersPage contains any results. func (page ResourceProvidersPage) IsEmpty() (bool, error) { + if page.StatusCode == 204 { + return true, nil + } + resourceProviders, err := ExtractResourceProviders(page) return len(resourceProviders) == 0, err } diff --git a/openstack/sharedfilesystems/apiversions/results.go b/openstack/sharedfilesystems/apiversions/results.go index 60c1f1b3ab..0c915b8f52 100644 --- a/openstack/sharedfilesystems/apiversions/results.go +++ b/openstack/sharedfilesystems/apiversions/results.go @@ -33,6 +33,10 @@ type APIVersionPage struct { // IsEmpty checks whether an APIVersionPage struct is empty. func (r APIVersionPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + is, err := ExtractAPIVersions(r) return len(is) == 0, err } diff --git a/openstack/sharedfilesystems/v2/messages/results.go b/openstack/sharedfilesystems/v2/messages/results.go index 9c48ea07b8..8507454bfa 100644 --- a/openstack/sharedfilesystems/v2/messages/results.go +++ b/openstack/sharedfilesystems/v2/messages/results.go @@ -65,6 +65,10 @@ type MessagePage struct { // IsEmpty returns true if a ListResult contains no Messages. func (r MessagePage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + messages, err := ExtractMessages(r) return len(messages) == 0, err } diff --git a/openstack/sharedfilesystems/v2/schedulerstats/results.go b/openstack/sharedfilesystems/v2/schedulerstats/results.go index c3ef678b52..671a314abb 100644 --- a/openstack/sharedfilesystems/v2/schedulerstats/results.go +++ b/openstack/sharedfilesystems/v2/schedulerstats/results.go @@ -101,6 +101,10 @@ type PoolPage struct { // IsEmpty satisfies the IsEmpty method of the Page interface. It returns true // if a List contains no results. func (page PoolPage) IsEmpty() (bool, error) { + if page.StatusCode == 204 { + return true, nil + } + va, err := ExtractPools(page) return len(va) == 0, err } diff --git a/openstack/sharedfilesystems/v2/securityservices/results.go b/openstack/sharedfilesystems/v2/securityservices/results.go index 355f7c76a2..0f510f1570 100644 --- a/openstack/sharedfilesystems/v2/securityservices/results.go +++ b/openstack/sharedfilesystems/v2/securityservices/results.go @@ -71,6 +71,10 @@ type SecurityServicePage struct { // IsEmpty returns true if a ListResult contains no SecurityServices. func (r SecurityServicePage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + securityServices, err := ExtractSecurityServices(r) return len(securityServices) == 0, err } diff --git a/openstack/sharedfilesystems/v2/services/results.go b/openstack/sharedfilesystems/v2/services/results.go index ef498a5b51..03ecfda897 100644 --- a/openstack/sharedfilesystems/v2/services/results.go +++ b/openstack/sharedfilesystems/v2/services/results.go @@ -57,6 +57,10 @@ type ServicePage struct { // IsEmpty determines whether or not a page of Services contains any results. func (page ServicePage) IsEmpty() (bool, error) { + if page.StatusCode == 204 { + return true, nil + } + services, err := ExtractServices(page) return len(services) == 0, err } diff --git a/openstack/sharedfilesystems/v2/sharenetworks/results.go b/openstack/sharedfilesystems/v2/sharenetworks/results.go index fdb7256953..76c35f78d6 100644 --- a/openstack/sharedfilesystems/v2/sharenetworks/results.go +++ b/openstack/sharedfilesystems/v2/sharenetworks/results.go @@ -126,6 +126,10 @@ func (r ShareNetworkPage) LastMarker() (string, error) { // IsEmpty satisifies the IsEmpty method of the Page interface func (r ShareNetworkPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + shareNetworks, err := ExtractShareNetworks(r) return len(shareNetworks) == 0, err } diff --git a/openstack/sharedfilesystems/v2/shares/results.go b/openstack/sharedfilesystems/v2/shares/results.go index d2e7470bbb..3204481a8f 100644 --- a/openstack/sharedfilesystems/v2/shares/results.go +++ b/openstack/sharedfilesystems/v2/shares/results.go @@ -178,6 +178,10 @@ func (r SharePage) LastMarker() (string, error) { // IsEmpty satisifies the IsEmpty method of the Page interface func (r SharePage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + shares, err := ExtractShares(r) return len(shares) == 0, err } diff --git a/openstack/sharedfilesystems/v2/sharetypes/results.go b/openstack/sharedfilesystems/v2/sharetypes/results.go index f60d757766..ce5fbccb03 100644 --- a/openstack/sharedfilesystems/v2/sharetypes/results.go +++ b/openstack/sharedfilesystems/v2/sharetypes/results.go @@ -50,6 +50,10 @@ type ShareTypePage struct { // IsEmpty returns true if a ListResult contains no ShareTypes. func (r ShareTypePage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + shareTypes, err := ExtractShareTypes(r) return len(shareTypes) == 0, err } diff --git a/openstack/sharedfilesystems/v2/snapshots/results.go b/openstack/sharedfilesystems/v2/snapshots/results.go index 6b4bb9b95c..a3d45aaa94 100644 --- a/openstack/sharedfilesystems/v2/snapshots/results.go +++ b/openstack/sharedfilesystems/v2/snapshots/results.go @@ -139,6 +139,10 @@ func (r SnapshotPage) LastMarker() (string, error) { // IsEmpty satisifies the IsEmpty method of the Page interface func (r SnapshotPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + snapshots, err := ExtractSnapshots(r) return len(snapshots) == 0, err } diff --git a/openstack/workflow/v2/crontriggers/results.go b/openstack/workflow/v2/crontriggers/results.go index 5a1b4f2485..2e548f4836 100644 --- a/openstack/workflow/v2/crontriggers/results.go +++ b/openstack/workflow/v2/crontriggers/results.go @@ -130,6 +130,10 @@ type CronTriggerPage struct { // IsEmpty checks if an CronTriggerPage contains any results. func (r CronTriggerPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + exec, err := ExtractCronTriggers(r) return len(exec) == 0, err } diff --git a/openstack/workflow/v2/executions/results.go b/openstack/workflow/v2/executions/results.go index 0c73370859..b0e88f1255 100644 --- a/openstack/workflow/v2/executions/results.go +++ b/openstack/workflow/v2/executions/results.go @@ -132,6 +132,10 @@ type ExecutionPage struct { // IsEmpty checks if an ExecutionPage contains any results. func (r ExecutionPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + exec, err := ExtractExecutions(r) return len(exec) == 0, err } diff --git a/openstack/workflow/v2/workflows/results.go b/openstack/workflow/v2/workflows/results.go index 1e4803e877..d2063ae5cd 100644 --- a/openstack/workflow/v2/workflows/results.go +++ b/openstack/workflow/v2/workflows/results.go @@ -106,6 +106,10 @@ type WorkflowPage struct { // IsEmpty checks if an WorkflowPage contains any results. func (r WorkflowPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + exec, err := ExtractWorkflows(r) return len(exec) == 0, err } From bd3de1414abcbd65627ef7ee90969273cf919901 Mon Sep 17 00:00:00 2001 From: galaxy <1536857028@qq.com> Date: Fri, 10 Mar 2023 17:04:32 +0800 Subject: [PATCH 215/360] fix: Incorrect Documentation --- openstack/networking/v2/extensions/provider/doc.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openstack/networking/v2/extensions/provider/doc.go b/openstack/networking/v2/extensions/provider/doc.go index ddc44175a7..8700a30e53 100644 --- a/openstack/networking/v2/extensions/provider/doc.go +++ b/openstack/networking/v2/extensions/provider/doc.go @@ -60,7 +60,7 @@ Example to Create a Provider Network Shared: &iTrue, } - createOpts : provider.CreateOptsExt{ + createOpts := provider.CreateOptsExt{ CreateOptsBuilder: networkCreateOpts, Segments: segments, } From a50a6e54349883f61e38261c8b3a60cb9fb0e6f8 Mon Sep 17 00:00:00 2001 From: Emil Maruszczak <emil.maruszczak@thehutgroup.com> Date: Fri, 10 Mar 2023 16:34:28 +0100 Subject: [PATCH 216/360] Skip below xena and add explanation --- acceptance/openstack/identity/v3/limits_test.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/acceptance/openstack/identity/v3/limits_test.go b/acceptance/openstack/identity/v3/limits_test.go index 672787c273..a988c0aec6 100644 --- a/acceptance/openstack/identity/v3/limits_test.go +++ b/acceptance/openstack/identity/v3/limits_test.go @@ -42,6 +42,11 @@ func TestLimitsList(t *testing.T) { } func TestCreateLimits(t *testing.T) { + // TODO: After https://github.com/gophercloud/gophercloud/issues/2290 is implemented + // use registered limits API to create global registered limit and then overwrite it with limit. + // Current solution (using glance limit) only works with Openstack Xena and above. + clients.SkipReleasesBelow(t, "stable/xena") + err := os.Setenv("OS_SYSTEM_SCOPE", "all") th.AssertNoErr(t, err) defer os.Unsetenv("OS_SYSTEM_SCOPE") @@ -59,7 +64,6 @@ func TestCreateLimits(t *testing.T) { th.AssertNoErr(t, err) // Find image service (glance on Devstack) which has precreated registered limits. - // TODO: Use registered limits API to create global limit and then overwrite it with limit. allPages, err := services.List(client, nil).AllPages() th.AssertNoErr(t, err) From d2d5278141a0065f8eeca2d0695bf05d03e67b52 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Mar 2023 10:04:39 +0000 Subject: [PATCH 217/360] Bump EmilienM/devstack-action from 0.10 to 0.11 Bumps [EmilienM/devstack-action](https://github.com/EmilienM/devstack-action) from 0.10 to 0.11. - [Release notes](https://github.com/EmilienM/devstack-action/releases) - [Commits](https://github.com/EmilienM/devstack-action/compare/v0.10...v0.11) --- updated-dependencies: - dependency-name: EmilienM/devstack-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> --- .github/workflows/functional-baremetal.yaml | 2 +- .github/workflows/functional-basic.yaml | 2 +- .github/workflows/functional-blockstorage.yaml | 2 +- .github/workflows/functional-clustering.yaml | 2 +- .github/workflows/functional-compute.yaml | 2 +- .github/workflows/functional-containerinfra.yaml | 2 +- .github/workflows/functional-dns.yaml | 2 +- .github/workflows/functional-identity.yaml | 2 +- .github/workflows/functional-imageservice.yaml | 2 +- .github/workflows/functional-keymanager.yaml | 2 +- .github/workflows/functional-loadbalancer.yaml | 2 +- .github/workflows/functional-messaging.yaml | 2 +- .github/workflows/functional-networking.yaml | 2 +- .github/workflows/functional-objectstorage.yaml | 2 +- .github/workflows/functional-orchestration.yaml | 2 +- .github/workflows/functional-placement.yaml | 2 +- .github/workflows/functional-sharedfilesystems.yaml | 2 +- 17 files changed, 17 insertions(+), 17 deletions(-) diff --git a/.github/workflows/functional-baremetal.yaml b/.github/workflows/functional-baremetal.yaml index f9148a4f85..2a07f386cb 100644 --- a/.github/workflows/functional-baremetal.yaml +++ b/.github/workflows/functional-baremetal.yaml @@ -35,7 +35,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.10 + uses: EmilienM/devstack-action@v0.11 with: branch: ${{ matrix.openstack_version }} conf_overrides: | diff --git a/.github/workflows/functional-basic.yaml b/.github/workflows/functional-basic.yaml index b0f5e49dd8..a35284f906 100644 --- a/.github/workflows/functional-basic.yaml +++ b/.github/workflows/functional-basic.yaml @@ -41,7 +41,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.10 + uses: EmilienM/devstack-action@v0.11 with: branch: ${{ matrix.openstack_version }} enabled_services: 's-account,s-container,s-object,s-proxy' diff --git a/.github/workflows/functional-blockstorage.yaml b/.github/workflows/functional-blockstorage.yaml index fc935cbadd..89911aa40b 100644 --- a/.github/workflows/functional-blockstorage.yaml +++ b/.github/workflows/functional-blockstorage.yaml @@ -38,7 +38,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.10 + uses: EmilienM/devstack-action@v0.11 with: branch: ${{ matrix.openstack_version }} conf_overrides: | diff --git a/.github/workflows/functional-clustering.yaml b/.github/workflows/functional-clustering.yaml index 65521f14e0..e7bb003e2a 100644 --- a/.github/workflows/functional-clustering.yaml +++ b/.github/workflows/functional-clustering.yaml @@ -38,7 +38,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.10 + uses: EmilienM/devstack-action@v0.11 with: branch: ${{ matrix.openstack_version }} conf_overrides: | diff --git a/.github/workflows/functional-compute.yaml b/.github/workflows/functional-compute.yaml index 5e51868d70..a6a71bcada 100644 --- a/.github/workflows/functional-compute.yaml +++ b/.github/workflows/functional-compute.yaml @@ -38,7 +38,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.10 + uses: EmilienM/devstack-action@v0.11 with: branch: ${{ matrix.openstack_version }} conf_overrides: | diff --git a/.github/workflows/functional-containerinfra.yaml b/.github/workflows/functional-containerinfra.yaml index d444a9cfd3..db2d6b64f0 100644 --- a/.github/workflows/functional-containerinfra.yaml +++ b/.github/workflows/functional-containerinfra.yaml @@ -52,7 +52,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.10 + uses: EmilienM/devstack-action@v0.11 with: branch: ${{ matrix.openstack_version }} conf_overrides: | diff --git a/.github/workflows/functional-dns.yaml b/.github/workflows/functional-dns.yaml index b7509c6b2e..f9c9bfa9ae 100644 --- a/.github/workflows/functional-dns.yaml +++ b/.github/workflows/functional-dns.yaml @@ -53,7 +53,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.10 + uses: EmilienM/devstack-action@v0.11 with: branch: ${{ matrix.openstack_version }} conf_overrides: | diff --git a/.github/workflows/functional-identity.yaml b/.github/workflows/functional-identity.yaml index 2efa905949..379430d478 100644 --- a/.github/workflows/functional-identity.yaml +++ b/.github/workflows/functional-identity.yaml @@ -38,7 +38,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.10 + uses: EmilienM/devstack-action@v0.11 with: branch: ${{ matrix.openstack_version }} - name: Checkout go diff --git a/.github/workflows/functional-imageservice.yaml b/.github/workflows/functional-imageservice.yaml index c505ea1212..8142ab0c89 100644 --- a/.github/workflows/functional-imageservice.yaml +++ b/.github/workflows/functional-imageservice.yaml @@ -38,7 +38,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.10 + uses: EmilienM/devstack-action@v0.11 with: branch: ${{ matrix.openstack_version }} - name: Checkout go diff --git a/.github/workflows/functional-keymanager.yaml b/.github/workflows/functional-keymanager.yaml index 799d4e63e1..3b997c439b 100644 --- a/.github/workflows/functional-keymanager.yaml +++ b/.github/workflows/functional-keymanager.yaml @@ -38,7 +38,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.10 + uses: EmilienM/devstack-action@v0.11 with: branch: ${{ matrix.openstack_version }} conf_overrides: | diff --git a/.github/workflows/functional-loadbalancer.yaml b/.github/workflows/functional-loadbalancer.yaml index 58173fa4bd..e2cb3dbe5d 100644 --- a/.github/workflows/functional-loadbalancer.yaml +++ b/.github/workflows/functional-loadbalancer.yaml @@ -38,7 +38,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.10 + uses: EmilienM/devstack-action@v0.11 with: branch: ${{ matrix.openstack_version }} conf_overrides: | diff --git a/.github/workflows/functional-messaging.yaml b/.github/workflows/functional-messaging.yaml index d1b137c784..a39d12e842 100644 --- a/.github/workflows/functional-messaging.yaml +++ b/.github/workflows/functional-messaging.yaml @@ -38,7 +38,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.10 + uses: EmilienM/devstack-action@v0.11 with: branch: ${{ matrix.openstack_version }} conf_overrides: | diff --git a/.github/workflows/functional-networking.yaml b/.github/workflows/functional-networking.yaml index ba18a010a7..6953ec6b73 100644 --- a/.github/workflows/functional-networking.yaml +++ b/.github/workflows/functional-networking.yaml @@ -61,7 +61,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.10 + uses: EmilienM/devstack-action@v0.11 with: branch: ${{ matrix.openstack_version }} conf_overrides: | diff --git a/.github/workflows/functional-objectstorage.yaml b/.github/workflows/functional-objectstorage.yaml index 3a52e4724f..dcd208f080 100644 --- a/.github/workflows/functional-objectstorage.yaml +++ b/.github/workflows/functional-objectstorage.yaml @@ -38,7 +38,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.10 + uses: EmilienM/devstack-action@v0.11 with: branch: ${{ matrix.openstack_version }} conf_overrides: | diff --git a/.github/workflows/functional-orchestration.yaml b/.github/workflows/functional-orchestration.yaml index 8c0bb984a9..6a6b13c233 100644 --- a/.github/workflows/functional-orchestration.yaml +++ b/.github/workflows/functional-orchestration.yaml @@ -38,7 +38,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.10 + uses: EmilienM/devstack-action@v0.11 with: branch: ${{ matrix.openstack_version }} conf_overrides: | diff --git a/.github/workflows/functional-placement.yaml b/.github/workflows/functional-placement.yaml index 1e59b583f9..20d13599ac 100644 --- a/.github/workflows/functional-placement.yaml +++ b/.github/workflows/functional-placement.yaml @@ -38,7 +38,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.10 + uses: EmilienM/devstack-action@v0.11 with: branch: ${{ matrix.openstack_version }} - name: Checkout go diff --git a/.github/workflows/functional-sharedfilesystems.yaml b/.github/workflows/functional-sharedfilesystems.yaml index 9b6178abee..ccba6edb8f 100644 --- a/.github/workflows/functional-sharedfilesystems.yaml +++ b/.github/workflows/functional-sharedfilesystems.yaml @@ -38,7 +38,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.10 + uses: EmilienM/devstack-action@v0.11 with: branch: ${{ matrix.openstack_version }} conf_overrides: | From a1187d3e4109d7dc5bf81c6ee4a5ab245a5f611a Mon Sep 17 00:00:00 2001 From: Mikael Johansson <mik.json@gmail.com> Date: Tue, 14 Mar 2023 11:38:32 +0100 Subject: [PATCH 218/360] Add missing rule protocol constants for IPIP (#2583) --- openstack/networking/v2/extensions/security/rules/requests.go | 1 + 1 file changed, 1 insertion(+) diff --git a/openstack/networking/v2/extensions/security/rules/requests.go b/openstack/networking/v2/extensions/security/rules/requests.go index 7b5331b6d0..364f7f5c98 100644 --- a/openstack/networking/v2/extensions/security/rules/requests.go +++ b/openstack/networking/v2/extensions/security/rules/requests.go @@ -60,6 +60,7 @@ const ( ProtocolGRE RuleProtocol = "gre" ProtocolICMP RuleProtocol = "icmp" ProtocolIGMP RuleProtocol = "igmp" + ProtocolIPIP RuleProtocol = "ipip" ProtocolIPv6Encap RuleProtocol = "ipv6-encap" ProtocolIPv6Frag RuleProtocol = "ipv6-frag" ProtocolIPv6ICMP RuleProtocol = "ipv6-icmp" From 6edc8004a78a8dd641cc92cbdd181b9085469864 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 16 Mar 2023 10:02:15 +0000 Subject: [PATCH 219/360] Bump actions/setup-go from 3 to 4 Bumps [actions/setup-go](https://github.com/actions/setup-go) from 3 to 4. - [Release notes](https://github.com/actions/setup-go/releases) - [Commits](https://github.com/actions/setup-go/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/setup-go dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> --- .github/workflows/functional-baremetal.yaml | 2 +- .github/workflows/functional-basic.yaml | 2 +- .github/workflows/functional-blockstorage.yaml | 2 +- .github/workflows/functional-clustering.yaml | 2 +- .github/workflows/functional-compute.yaml | 2 +- .github/workflows/functional-containerinfra.yaml | 2 +- .github/workflows/functional-dns.yaml | 2 +- .github/workflows/functional-identity.yaml | 2 +- .github/workflows/functional-imageservice.yaml | 2 +- .github/workflows/functional-keymanager.yaml | 2 +- .github/workflows/functional-loadbalancer.yaml | 2 +- .github/workflows/functional-messaging.yaml | 2 +- .github/workflows/functional-networking.yaml | 2 +- .github/workflows/functional-objectstorage.yaml | 2 +- .github/workflows/functional-orchestration.yaml | 2 +- .github/workflows/functional-placement.yaml | 2 +- .github/workflows/functional-sharedfilesystems.yaml | 2 +- .github/workflows/gomod.yml | 2 +- .github/workflows/reauth-retests.yaml | 2 +- .github/workflows/unit.yml | 2 +- 20 files changed, 20 insertions(+), 20 deletions(-) diff --git a/.github/workflows/functional-baremetal.yaml b/.github/workflows/functional-baremetal.yaml index f9148a4f85..4ace81404a 100644 --- a/.github/workflows/functional-baremetal.yaml +++ b/.github/workflows/functional-baremetal.yaml @@ -74,7 +74,7 @@ jobs: SWIFT_TEMPURL_KEY=secretkey enabled_services: 'ir-api,ir-cond,s-account,s-container,s-object,s-proxy,q-svc,q-agt,q-dhcp,q-l3,q-meta,-cinder,-c-sch,-c-api,-c-vol,-c-bak,-ovn,-ovn-controller,-ovn-northd,-q-ovn-metadata-agent' - name: Checkout go - uses: actions/setup-go@v3 + uses: actions/setup-go@v4 with: go-version: '^1.15' - name: Run Gophercloud acceptance tests diff --git a/.github/workflows/functional-basic.yaml b/.github/workflows/functional-basic.yaml index b0f5e49dd8..e379157e94 100644 --- a/.github/workflows/functional-basic.yaml +++ b/.github/workflows/functional-basic.yaml @@ -46,7 +46,7 @@ jobs: branch: ${{ matrix.openstack_version }} enabled_services: 's-account,s-container,s-object,s-proxy' - name: Checkout go - uses: actions/setup-go@v3 + uses: actions/setup-go@v4 with: go-version: '^1.15' - name: Run Gophercloud acceptance tests diff --git a/.github/workflows/functional-blockstorage.yaml b/.github/workflows/functional-blockstorage.yaml index fc935cbadd..de5025e3d9 100644 --- a/.github/workflows/functional-blockstorage.yaml +++ b/.github/workflows/functional-blockstorage.yaml @@ -45,7 +45,7 @@ jobs: CINDER_ISCSI_HELPER=tgtadm enabled_services: 's-account,s-container,s-object,s-proxy,c-bak' - name: Checkout go - uses: actions/setup-go@v3 + uses: actions/setup-go@v4 with: go-version: '^1.15' - name: Run Gophercloud acceptance tests diff --git a/.github/workflows/functional-clustering.yaml b/.github/workflows/functional-clustering.yaml index 65521f14e0..2536f46f4a 100644 --- a/.github/workflows/functional-clustering.yaml +++ b/.github/workflows/functional-clustering.yaml @@ -46,7 +46,7 @@ jobs: enable_plugin zaqar https://github.com/openstack/zaqar ${{ matrix.openstack_version }} ZAQARCLIENT_BRANCH=${{ matrix.openstack_version }} - name: Checkout go - uses: actions/setup-go@v3 + uses: actions/setup-go@v4 with: go-version: '^1.15' - name: Run Gophercloud acceptance tests diff --git a/.github/workflows/functional-compute.yaml b/.github/workflows/functional-compute.yaml index 5e51868d70..f5ee67af74 100644 --- a/.github/workflows/functional-compute.yaml +++ b/.github/workflows/functional-compute.yaml @@ -44,7 +44,7 @@ jobs: conf_overrides: | CINDER_ISCSI_HELPER=tgtadm - name: Checkout go - uses: actions/setup-go@v3 + uses: actions/setup-go@v4 with: go-version: '^1.15' - name: Run Gophercloud acceptance tests diff --git a/.github/workflows/functional-containerinfra.yaml b/.github/workflows/functional-containerinfra.yaml index d444a9cfd3..381976a932 100644 --- a/.github/workflows/functional-containerinfra.yaml +++ b/.github/workflows/functional-containerinfra.yaml @@ -65,7 +65,7 @@ jobs: ${{ matrix.devstack_conf_overrides }} enabled_services: 'h-eng,h-api,h-api-cfn,h-api-cw' - name: Checkout go - uses: actions/setup-go@v3 + uses: actions/setup-go@v4 with: go-version: '^1.15' - name: Run Gophercloud acceptance tests diff --git a/.github/workflows/functional-dns.yaml b/.github/workflows/functional-dns.yaml index b7509c6b2e..04a0544f1f 100644 --- a/.github/workflows/functional-dns.yaml +++ b/.github/workflows/functional-dns.yaml @@ -60,7 +60,7 @@ jobs: ${{ matrix.devstack_conf_overrides }} enabled_services: 'designate,designate-central,designate-api,designate-worker,designate-producer,designate-mdns' - name: Checkout go - uses: actions/setup-go@v3 + uses: actions/setup-go@v4 with: go-version: '^1.15' - name: Run Gophercloud acceptance tests diff --git a/.github/workflows/functional-identity.yaml b/.github/workflows/functional-identity.yaml index 2efa905949..12d0fb8b85 100644 --- a/.github/workflows/functional-identity.yaml +++ b/.github/workflows/functional-identity.yaml @@ -42,7 +42,7 @@ jobs: with: branch: ${{ matrix.openstack_version }} - name: Checkout go - uses: actions/setup-go@v3 + uses: actions/setup-go@v4 with: go-version: '^1.15' - name: Run Gophercloud acceptance tests diff --git a/.github/workflows/functional-imageservice.yaml b/.github/workflows/functional-imageservice.yaml index c505ea1212..4fbdaae267 100644 --- a/.github/workflows/functional-imageservice.yaml +++ b/.github/workflows/functional-imageservice.yaml @@ -42,7 +42,7 @@ jobs: with: branch: ${{ matrix.openstack_version }} - name: Checkout go - uses: actions/setup-go@v3 + uses: actions/setup-go@v4 with: go-version: '^1.15' - name: Run Gophercloud acceptance tests diff --git a/.github/workflows/functional-keymanager.yaml b/.github/workflows/functional-keymanager.yaml index 799d4e63e1..dab259a5c8 100644 --- a/.github/workflows/functional-keymanager.yaml +++ b/.github/workflows/functional-keymanager.yaml @@ -45,7 +45,7 @@ jobs: enable_plugin barbican https://github.com/openstack/barbican ${{ matrix.openstack_version }} enabled_services: 'barbican-svc,barbican-retry,barbican-keystone-listener' - name: Checkout go - uses: actions/setup-go@v3 + uses: actions/setup-go@v4 with: go-version: '^1.15' - name: Run Gophercloud acceptance tests diff --git a/.github/workflows/functional-loadbalancer.yaml b/.github/workflows/functional-loadbalancer.yaml index 58173fa4bd..28331cd063 100644 --- a/.github/workflows/functional-loadbalancer.yaml +++ b/.github/workflows/functional-loadbalancer.yaml @@ -46,7 +46,7 @@ jobs: enable_plugin neutron https://github.com/openstack/neutron ${{ matrix.openstack_version }} enabled_services: 'octavia,o-api,o-cw,o-hk,o-hm,o-da,neutron-qos' - name: Checkout go - uses: actions/setup-go@v3 + uses: actions/setup-go@v4 with: go-version: '^1.15' - name: Run Gophercloud acceptance tests diff --git a/.github/workflows/functional-messaging.yaml b/.github/workflows/functional-messaging.yaml index d1b137c784..8ceece066e 100644 --- a/.github/workflows/functional-messaging.yaml +++ b/.github/workflows/functional-messaging.yaml @@ -45,7 +45,7 @@ jobs: enable_plugin zaqar https://github.com/openstack/zaqar ${{ matrix.openstack_version }} ZAQARCLIENT_BRANCH=${{ matrix.openstack_version }} - name: Checkout go - uses: actions/setup-go@v3 + uses: actions/setup-go@v4 with: go-version: '^1.15' - name: Run Gophercloud acceptance tests diff --git a/.github/workflows/functional-networking.yaml b/.github/workflows/functional-networking.yaml index ba18a010a7..c09c746d61 100644 --- a/.github/workflows/functional-networking.yaml +++ b/.github/workflows/functional-networking.yaml @@ -69,7 +69,7 @@ jobs: ${{ matrix.devstack_conf_overrides }} enabled_services: 'neutron-dns,neutron-qos,neutron-segments,neutron-trunk,neutron-uplink-status-propagation,neutron-network-segment-range,neutron-port-forwarding' - name: Checkout go - uses: actions/setup-go@v3 + uses: actions/setup-go@v4 with: go-version: '^1.15' - name: Run Gophercloud acceptance tests diff --git a/.github/workflows/functional-objectstorage.yaml b/.github/workflows/functional-objectstorage.yaml index 3a52e4724f..230af1dbcb 100644 --- a/.github/workflows/functional-objectstorage.yaml +++ b/.github/workflows/functional-objectstorage.yaml @@ -49,7 +49,7 @@ jobs: allow_object_versioning = true enabled_services: 's-account,s-container,s-object,s-proxy' - name: Checkout go - uses: actions/setup-go@v3 + uses: actions/setup-go@v4 with: go-version: '^1.15' - name: Run Gophercloud acceptance tests diff --git a/.github/workflows/functional-orchestration.yaml b/.github/workflows/functional-orchestration.yaml index 8c0bb984a9..58d682f17b 100644 --- a/.github/workflows/functional-orchestration.yaml +++ b/.github/workflows/functional-orchestration.yaml @@ -45,7 +45,7 @@ jobs: enable_plugin heat https://github.com/openstack/heat ${{ matrix.openstack_version }} enabled_services: 'h-eng,h-api,h-api-cfn,h-api-cw' - name: Checkout go - uses: actions/setup-go@v3 + uses: actions/setup-go@v4 with: go-version: '^1.15' - name: Run Gophercloud acceptance tests diff --git a/.github/workflows/functional-placement.yaml b/.github/workflows/functional-placement.yaml index 1e59b583f9..a2593d7ecd 100644 --- a/.github/workflows/functional-placement.yaml +++ b/.github/workflows/functional-placement.yaml @@ -42,7 +42,7 @@ jobs: with: branch: ${{ matrix.openstack_version }} - name: Checkout go - uses: actions/setup-go@v3 + uses: actions/setup-go@v4 with: go-version: '^1.15' - name: Run Gophercloud acceptance tests diff --git a/.github/workflows/functional-sharedfilesystems.yaml b/.github/workflows/functional-sharedfilesystems.yaml index 9b6178abee..2cf789cbb3 100644 --- a/.github/workflows/functional-sharedfilesystems.yaml +++ b/.github/workflows/functional-sharedfilesystems.yaml @@ -58,7 +58,7 @@ jobs: MANILA_CONFIGURE_DEFAULT_TYPES=True MANILA_INSTALL_TEMPEST_PLUGIN_SYSTEMWIDE=false - name: Checkout go - uses: actions/setup-go@v3 + uses: actions/setup-go@v4 with: go-version: '^1.15' - name: Run Gophercloud acceptance tests diff --git a/.github/workflows/gomod.yml b/.github/workflows/gomod.yml index 34530efbdc..929bad77e9 100644 --- a/.github/workflows/gomod.yml +++ b/.github/workflows/gomod.yml @@ -8,7 +8,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - uses: actions/setup-go@v3 + - uses: actions/setup-go@v4 with: go-version: '1' - run: if [ $(go mod tidy && git diff | wc -l) -gt 0 ]; then git diff && exit 1; fi diff --git a/.github/workflows/reauth-retests.yaml b/.github/workflows/reauth-retests.yaml index 80e8cd06d2..ae1aa17994 100644 --- a/.github/workflows/reauth-retests.yaml +++ b/.github/workflows/reauth-retests.yaml @@ -14,7 +14,7 @@ jobs: steps: - name: Setup Go ${{ matrix.go-version }} - uses: actions/setup-go@v3 + uses: actions/setup-go@v4 with: go-version: ${{ matrix.go-version }} diff --git a/.github/workflows/unit.yml b/.github/workflows/unit.yml index dd6832b2af..9824f624ca 100644 --- a/.github/workflows/unit.yml +++ b/.github/workflows/unit.yml @@ -21,7 +21,7 @@ jobs: steps: - name: Setup Go ${{ matrix.go-version }} - uses: actions/setup-go@v3 + uses: actions/setup-go@v4 with: go-version: ${{ matrix.go-version }} From ffc0d5f4ebb256cb5638898cfd1eb9fb82d0c088 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= <martin.andre@gmail.com> Date: Sat, 4 Mar 2023 12:45:50 +0100 Subject: [PATCH 220/360] Add CI jobs for the zed release --- .github/workflows/functional-baremetal.yaml | 3 +++ .github/workflows/functional-basic.yaml | 3 +++ .github/workflows/functional-blockstorage.yaml | 3 +++ .github/workflows/functional-clustering.yaml | 3 +++ .github/workflows/functional-compute.yaml | 3 +++ .github/workflows/functional-containerinfra.yaml | 5 +++++ .github/workflows/functional-dns.yaml | 5 +++++ .github/workflows/functional-identity.yaml | 3 +++ .github/workflows/functional-imageservice.yaml | 3 +++ .github/workflows/functional-keymanager.yaml | 3 +++ .github/workflows/functional-loadbalancer.yaml | 3 +++ .github/workflows/functional-messaging.yaml | 3 +++ .github/workflows/functional-networking.yaml | 6 ++++++ .github/workflows/functional-objectstorage.yaml | 3 +++ .github/workflows/functional-orchestration.yaml | 3 +++ .github/workflows/functional-placement.yaml | 3 +++ .github/workflows/functional-sharedfilesystems.yaml | 3 +++ 17 files changed, 58 insertions(+) diff --git a/.github/workflows/functional-baremetal.yaml b/.github/workflows/functional-baremetal.yaml index 4ace81404a..857b7e2f96 100644 --- a/.github/workflows/functional-baremetal.yaml +++ b/.github/workflows/functional-baremetal.yaml @@ -14,6 +14,9 @@ jobs: openstack_version: ["master"] ubuntu_version: ["20.04"] include: + - name: "zed" + openstack_version: "stable/zed" + ubuntu_version: "20.04" - name: "yoga" openstack_version: "stable/yoga" ubuntu_version: "20.04" diff --git a/.github/workflows/functional-basic.yaml b/.github/workflows/functional-basic.yaml index e379157e94..4d2a3b81b0 100644 --- a/.github/workflows/functional-basic.yaml +++ b/.github/workflows/functional-basic.yaml @@ -17,6 +17,9 @@ jobs: openstack_version: ["master"] ubuntu_version: ["20.04"] include: + - name: "zed" + openstack_version: "stable/zed" + ubuntu_version: "20.04" - name: "yoga" openstack_version: "stable/yoga" ubuntu_version: "20.04" diff --git a/.github/workflows/functional-blockstorage.yaml b/.github/workflows/functional-blockstorage.yaml index de5025e3d9..bc74be229e 100644 --- a/.github/workflows/functional-blockstorage.yaml +++ b/.github/workflows/functional-blockstorage.yaml @@ -14,6 +14,9 @@ jobs: openstack_version: ["master"] ubuntu_version: ["20.04"] include: + - name: "zed" + openstack_version: "stable/zed" + ubuntu_version: "20.04" - name: "yoga" openstack_version: "stable/yoga" ubuntu_version: "20.04" diff --git a/.github/workflows/functional-clustering.yaml b/.github/workflows/functional-clustering.yaml index 2536f46f4a..d40de9e8f3 100644 --- a/.github/workflows/functional-clustering.yaml +++ b/.github/workflows/functional-clustering.yaml @@ -14,6 +14,9 @@ jobs: openstack_version: ["master"] ubuntu_version: ["20.04"] include: + - name: "zed" + openstack_version: "stable/zed" + ubuntu_version: "20.04" - name: "yoga" openstack_version: "stable/yoga" ubuntu_version: "20.04" diff --git a/.github/workflows/functional-compute.yaml b/.github/workflows/functional-compute.yaml index f5ee67af74..96a3575ddd 100644 --- a/.github/workflows/functional-compute.yaml +++ b/.github/workflows/functional-compute.yaml @@ -14,6 +14,9 @@ jobs: openstack_version: ["master"] ubuntu_version: ["20.04"] include: + - name: "zed" + openstack_version: "stable/zed" + ubuntu_version: "20.04" - name: "yoga" openstack_version: "stable/yoga" ubuntu_version: "20.04" diff --git a/.github/workflows/functional-containerinfra.yaml b/.github/workflows/functional-containerinfra.yaml index 381976a932..c900dba356 100644 --- a/.github/workflows/functional-containerinfra.yaml +++ b/.github/workflows/functional-containerinfra.yaml @@ -16,6 +16,11 @@ jobs: ubuntu_version: "20.04" devstack_conf_overrides: | enable_plugin magnum https://github.com/openstack/magnum master + - name: "zed" + openstack_version: "stable/zed" + ubuntu_version: "20.04" + devstack_conf_overrides: | + enable_plugin magnum https://github.com/openstack/magnum stable/zed - name: "yoga" openstack_version: "stable/yoga" ubuntu_version: "20.04" diff --git a/.github/workflows/functional-dns.yaml b/.github/workflows/functional-dns.yaml index 04a0544f1f..c250aa8c48 100644 --- a/.github/workflows/functional-dns.yaml +++ b/.github/workflows/functional-dns.yaml @@ -17,6 +17,11 @@ jobs: ubuntu_version: "20.04" devstack_conf_overrides: | enable_plugin designate https://github.com/openstack/designate master + - name: "zed" + openstack_version: "stable/zed" + ubuntu_version: "20.04" + devstack_conf_overrides: | + enable_plugin designate https://github.com/openstack/designate stable/zed - name: "yoga" openstack_version: "stable/yoga" ubuntu_version: "20.04" diff --git a/.github/workflows/functional-identity.yaml b/.github/workflows/functional-identity.yaml index 12d0fb8b85..989b36532c 100644 --- a/.github/workflows/functional-identity.yaml +++ b/.github/workflows/functional-identity.yaml @@ -14,6 +14,9 @@ jobs: openstack_version: ["master"] ubuntu_version: ["20.04"] include: + - name: "zed" + openstack_version: "stable/zed" + ubuntu_version: "20.04" - name: "yoga" openstack_version: "stable/yoga" ubuntu_version: "20.04" diff --git a/.github/workflows/functional-imageservice.yaml b/.github/workflows/functional-imageservice.yaml index 4fbdaae267..c3156197ab 100644 --- a/.github/workflows/functional-imageservice.yaml +++ b/.github/workflows/functional-imageservice.yaml @@ -14,6 +14,9 @@ jobs: openstack_version: ["master"] ubuntu_version: ["20.04"] include: + - name: "zed" + openstack_version: "stable/zed" + ubuntu_version: "20.04" - name: "yoga" openstack_version: "stable/yoga" ubuntu_version: "20.04" diff --git a/.github/workflows/functional-keymanager.yaml b/.github/workflows/functional-keymanager.yaml index dab259a5c8..d75af6b8b7 100644 --- a/.github/workflows/functional-keymanager.yaml +++ b/.github/workflows/functional-keymanager.yaml @@ -14,6 +14,9 @@ jobs: openstack_version: ["master"] ubuntu_version: ["20.04"] include: + - name: "zed" + openstack_version: "stable/zed" + ubuntu_version: "20.04" - name: "yoga" openstack_version: "stable/yoga" ubuntu_version: "20.04" diff --git a/.github/workflows/functional-loadbalancer.yaml b/.github/workflows/functional-loadbalancer.yaml index 28331cd063..699f50a2e4 100644 --- a/.github/workflows/functional-loadbalancer.yaml +++ b/.github/workflows/functional-loadbalancer.yaml @@ -14,6 +14,9 @@ jobs: openstack_version: ["master"] ubuntu_version: ["20.04"] include: + - name: "zed" + openstack_version: "stable/zed" + ubuntu_version: "20.04" - name: "yoga" openstack_version: "stable/yoga" ubuntu_version: "20.04" diff --git a/.github/workflows/functional-messaging.yaml b/.github/workflows/functional-messaging.yaml index 8ceece066e..062ce3b4e5 100644 --- a/.github/workflows/functional-messaging.yaml +++ b/.github/workflows/functional-messaging.yaml @@ -14,6 +14,9 @@ jobs: openstack_version: ["master"] ubuntu_version: ["20.04"] include: + - name: "zed" + openstack_version: "stable/zed" + ubuntu_version: "20.04" - name: "yoga" openstack_version: "stable/yoga" ubuntu_version: "20.04" diff --git a/.github/workflows/functional-networking.yaml b/.github/workflows/functional-networking.yaml index c09c746d61..62231dfa98 100644 --- a/.github/workflows/functional-networking.yaml +++ b/.github/workflows/functional-networking.yaml @@ -17,6 +17,12 @@ jobs: devstack_conf_overrides: | enable_plugin neutron-dynamic-routing https://github.com/openstack/neutron-dynamic-routing master enable_plugin neutron-vpnaas https://github.com/openstack/neutron-vpnaas master + - name: "zed" + openstack_version: "stable/zed" + ubuntu_version: "20.04" + devstack_conf_overrides: | + enable_plugin neutron-dynamic-routing https://github.com/openstack/neutron-dynamic-routing stable/zed + enable_plugin neutron-vpnaas https://github.com/openstack/neutron-vpnaas stable/zed - name: "yoga" openstack_version: "stable/yoga" ubuntu_version: "20.04" diff --git a/.github/workflows/functional-objectstorage.yaml b/.github/workflows/functional-objectstorage.yaml index 230af1dbcb..a269d48488 100644 --- a/.github/workflows/functional-objectstorage.yaml +++ b/.github/workflows/functional-objectstorage.yaml @@ -14,6 +14,9 @@ jobs: openstack_version: ["master"] ubuntu_version: ["20.04"] include: + - name: "zed" + openstack_version: "stable/zed" + ubuntu_version: "20.04" - name: "yoga" openstack_version: "stable/yoga" ubuntu_version: "20.04" diff --git a/.github/workflows/functional-orchestration.yaml b/.github/workflows/functional-orchestration.yaml index 58d682f17b..4fa6201fd0 100644 --- a/.github/workflows/functional-orchestration.yaml +++ b/.github/workflows/functional-orchestration.yaml @@ -14,6 +14,9 @@ jobs: openstack_version: ["master"] ubuntu_version: ["20.04"] include: + - name: "zed" + openstack_version: "stable/zed" + ubuntu_version: "20.04" - name: "yoga" openstack_version: "stable/yoga" ubuntu_version: "20.04" diff --git a/.github/workflows/functional-placement.yaml b/.github/workflows/functional-placement.yaml index a2593d7ecd..849b6e9617 100644 --- a/.github/workflows/functional-placement.yaml +++ b/.github/workflows/functional-placement.yaml @@ -14,6 +14,9 @@ jobs: openstack_version: ["master"] ubuntu_version: ["20.04"] include: + - name: "zed" + openstack_version: "stable/zed" + ubuntu_version: "20.04" - name: "yoga" openstack_version: "stable/yoga" ubuntu_version: "20.04" diff --git a/.github/workflows/functional-sharedfilesystems.yaml b/.github/workflows/functional-sharedfilesystems.yaml index 2cf789cbb3..07f6852921 100644 --- a/.github/workflows/functional-sharedfilesystems.yaml +++ b/.github/workflows/functional-sharedfilesystems.yaml @@ -14,6 +14,9 @@ jobs: openstack_version: ["master"] ubuntu_version: ["20.04"] include: + - name: "zed" + openstack_version: "stable/zed" + ubuntu_version: "20.04" - name: "yoga" openstack_version: "stable/yoga" ubuntu_version: "20.04" From b1452f990bc05b132d7ca5db99ba0f20ff3642dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= <martin.andre@gmail.com> Date: Sat, 4 Mar 2023 12:48:49 +0100 Subject: [PATCH 221/360] Reduce frequency of periodic jobs There's no good reason for running the periodic jobs as frequently as everyday given the pace of development of openstack and how fast our small team can react to regressions. Reduce the frequency to once every 3 days, which might still be way to much. --- .github/workflows/functional-baremetal.yaml | 2 +- .github/workflows/functional-basic.yaml | 2 +- .github/workflows/functional-blockstorage.yaml | 2 +- .github/workflows/functional-clustering.yaml | 2 +- .github/workflows/functional-compute.yaml | 2 +- .github/workflows/functional-containerinfra.yaml | 2 +- .github/workflows/functional-dns.yaml | 2 +- .github/workflows/functional-identity.yaml | 2 +- .github/workflows/functional-imageservice.yaml | 2 +- .github/workflows/functional-keymanager.yaml | 2 +- .github/workflows/functional-loadbalancer.yaml | 2 +- .github/workflows/functional-messaging.yaml | 2 +- .github/workflows/functional-networking.yaml | 2 +- .github/workflows/functional-objectstorage.yaml | 2 +- .github/workflows/functional-orchestration.yaml | 2 +- .github/workflows/functional-placement.yaml | 2 +- .github/workflows/functional-sharedfilesystems.yaml | 2 +- 17 files changed, 17 insertions(+), 17 deletions(-) diff --git a/.github/workflows/functional-baremetal.yaml b/.github/workflows/functional-baremetal.yaml index 857b7e2f96..c4f83c2edf 100644 --- a/.github/workflows/functional-baremetal.yaml +++ b/.github/workflows/functional-baremetal.yaml @@ -4,7 +4,7 @@ on: paths: - '**baremetal**' schedule: - - cron: '0 0 * * *' + - cron: '0 0 */3 * *' jobs: functional-baremetal: strategy: diff --git a/.github/workflows/functional-basic.yaml b/.github/workflows/functional-basic.yaml index 4d2a3b81b0..22fade11ee 100644 --- a/.github/workflows/functional-basic.yaml +++ b/.github/workflows/functional-basic.yaml @@ -7,7 +7,7 @@ on: - '**.gitignore' - '**LICENSE' schedule: - - cron: '0 0 * * *' + - cron: '0 0 */3 * *' jobs: functional-basic: strategy: diff --git a/.github/workflows/functional-blockstorage.yaml b/.github/workflows/functional-blockstorage.yaml index bc74be229e..9f5dec6fcf 100644 --- a/.github/workflows/functional-blockstorage.yaml +++ b/.github/workflows/functional-blockstorage.yaml @@ -4,7 +4,7 @@ on: paths: - '**blockstorage**' schedule: - - cron: '0 0 * * *' + - cron: '0 0 */3 * *' jobs: functional-blockstorage: strategy: diff --git a/.github/workflows/functional-clustering.yaml b/.github/workflows/functional-clustering.yaml index d40de9e8f3..0dcca74e1b 100644 --- a/.github/workflows/functional-clustering.yaml +++ b/.github/workflows/functional-clustering.yaml @@ -4,7 +4,7 @@ on: paths: - '**clustering**' schedule: - - cron: '0 0 * * *' + - cron: '0 0 */3 * *' jobs: functional-clustering: strategy: diff --git a/.github/workflows/functional-compute.yaml b/.github/workflows/functional-compute.yaml index 96a3575ddd..de4ddc2c0b 100644 --- a/.github/workflows/functional-compute.yaml +++ b/.github/workflows/functional-compute.yaml @@ -4,7 +4,7 @@ on: paths: - '**compute**' schedule: - - cron: '0 0 * * *' + - cron: '0 0 */3 * *' jobs: functional-compute: strategy: diff --git a/.github/workflows/functional-containerinfra.yaml b/.github/workflows/functional-containerinfra.yaml index c900dba356..2ad9fd81f3 100644 --- a/.github/workflows/functional-containerinfra.yaml +++ b/.github/workflows/functional-containerinfra.yaml @@ -4,7 +4,7 @@ on: paths: - '**containerinfra**' schedule: - - cron: '0 0 * * *' + - cron: '0 0 */3 * *' jobs: functional-containerinfra: strategy: diff --git a/.github/workflows/functional-dns.yaml b/.github/workflows/functional-dns.yaml index c250aa8c48..81e4fc6888 100644 --- a/.github/workflows/functional-dns.yaml +++ b/.github/workflows/functional-dns.yaml @@ -5,7 +5,7 @@ on: - '**openstack/dns**' - '**functional-dns.yaml' schedule: - - cron: '0 0 * * *' + - cron: '0 0 */3 * *' jobs: functional-dns: strategy: diff --git a/.github/workflows/functional-identity.yaml b/.github/workflows/functional-identity.yaml index 989b36532c..a782edb028 100644 --- a/.github/workflows/functional-identity.yaml +++ b/.github/workflows/functional-identity.yaml @@ -4,7 +4,7 @@ on: paths: - '**identity**' schedule: - - cron: '0 0 * * *' + - cron: '0 0 */3 * *' jobs: functional-identity: strategy: diff --git a/.github/workflows/functional-imageservice.yaml b/.github/workflows/functional-imageservice.yaml index c3156197ab..2858258e49 100644 --- a/.github/workflows/functional-imageservice.yaml +++ b/.github/workflows/functional-imageservice.yaml @@ -4,7 +4,7 @@ on: paths: - '**imageservice**' schedule: - - cron: '0 0 * * *' + - cron: '0 0 */3 * *' jobs: functional-imageservice: strategy: diff --git a/.github/workflows/functional-keymanager.yaml b/.github/workflows/functional-keymanager.yaml index d75af6b8b7..6695771a9f 100644 --- a/.github/workflows/functional-keymanager.yaml +++ b/.github/workflows/functional-keymanager.yaml @@ -4,7 +4,7 @@ on: paths: - '**keymanager**' schedule: - - cron: '0 0 * * *' + - cron: '0 0 */3 * *' jobs: functional-keymanager: strategy: diff --git a/.github/workflows/functional-loadbalancer.yaml b/.github/workflows/functional-loadbalancer.yaml index 699f50a2e4..051fbf3d40 100644 --- a/.github/workflows/functional-loadbalancer.yaml +++ b/.github/workflows/functional-loadbalancer.yaml @@ -4,7 +4,7 @@ on: paths: - '**loadbalancer**' schedule: - - cron: '0 0 * * *' + - cron: '0 0 */3 * *' jobs: functional-loadbalancer: strategy: diff --git a/.github/workflows/functional-messaging.yaml b/.github/workflows/functional-messaging.yaml index 062ce3b4e5..1c812d84ad 100644 --- a/.github/workflows/functional-messaging.yaml +++ b/.github/workflows/functional-messaging.yaml @@ -4,7 +4,7 @@ on: paths: - '**messaging**' schedule: - - cron: '0 0 * * *' + - cron: '0 0 */3 * *' jobs: functional-messaging: strategy: diff --git a/.github/workflows/functional-networking.yaml b/.github/workflows/functional-networking.yaml index 62231dfa98..531c027eda 100644 --- a/.github/workflows/functional-networking.yaml +++ b/.github/workflows/functional-networking.yaml @@ -4,7 +4,7 @@ on: paths: - '**networking**' schedule: - - cron: '0 0 * * *' + - cron: '0 0 */3 * *' jobs: functional-networking: strategy: diff --git a/.github/workflows/functional-objectstorage.yaml b/.github/workflows/functional-objectstorage.yaml index a269d48488..3fe8059c13 100644 --- a/.github/workflows/functional-objectstorage.yaml +++ b/.github/workflows/functional-objectstorage.yaml @@ -4,7 +4,7 @@ on: paths: - '**objectstorage**' schedule: - - cron: '0 0 * * *' + - cron: '0 0 */3 * *' jobs: functional-objectstorage: strategy: diff --git a/.github/workflows/functional-orchestration.yaml b/.github/workflows/functional-orchestration.yaml index 4fa6201fd0..c2b6698153 100644 --- a/.github/workflows/functional-orchestration.yaml +++ b/.github/workflows/functional-orchestration.yaml @@ -4,7 +4,7 @@ on: paths: - '**orchestration**' schedule: - - cron: '0 0 * * *' + - cron: '0 0 */3 * *' jobs: functional-orchestration: strategy: diff --git a/.github/workflows/functional-placement.yaml b/.github/workflows/functional-placement.yaml index 849b6e9617..d2c2664c1c 100644 --- a/.github/workflows/functional-placement.yaml +++ b/.github/workflows/functional-placement.yaml @@ -4,7 +4,7 @@ on: paths: - '**placement**' schedule: - - cron: '0 0 * * *' + - cron: '0 0 */3 * *' jobs: functional-placement: strategy: diff --git a/.github/workflows/functional-sharedfilesystems.yaml b/.github/workflows/functional-sharedfilesystems.yaml index 07f6852921..e0778d45d0 100644 --- a/.github/workflows/functional-sharedfilesystems.yaml +++ b/.github/workflows/functional-sharedfilesystems.yaml @@ -4,7 +4,7 @@ on: paths: - '**sharedfilesystems**' schedule: - - cron: '0 0 * * *' + - cron: '0 0 */3 * *' jobs: functional-sharedfilesystems: strategy: From 81540a7da0d920712f32c33eb50a483da0d557ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= <m.andre@redhat.com> Date: Tue, 14 Mar 2023 11:39:17 +0100 Subject: [PATCH 222/360] Make TestMTUNetworkCRUDL deterministic It seems the MTU update API does not returned an object with the `updated_at` field to it's new value. This can cause our test to fail if we check the return of the Update function against the same object retrieved from a Get call, as the `updated_at` field can differ. Instead, just checked for the fields we change in this test. Fix #2592 --- acceptance/openstack/networking/v2/extensions/mtu/mtu_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/acceptance/openstack/networking/v2/extensions/mtu/mtu_test.go b/acceptance/openstack/networking/v2/extensions/mtu/mtu_test.go index 51b32a334d..8264d2d504 100644 --- a/acceptance/openstack/networking/v2/extensions/mtu/mtu_test.go +++ b/acceptance/openstack/networking/v2/extensions/mtu/mtu_test.go @@ -129,6 +129,7 @@ func TestMTUNetworkCRUDL(t *testing.T) { th.AssertNoErr(t, err) tools.PrintResource(t, getNewNetwork) - th.AssertDeepEquals(t, newNetwork, getNewNetwork) + th.AssertEquals(t, getNewNetwork.Description, newNetworkDescription) + th.AssertEquals(t, getNewNetwork.MTU, newNetworkMTU) } } From 228df1a304cb641234c6d7bab7b1dd895f9fe736 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= <m.andre@redhat.com> Date: Tue, 7 Mar 2023 15:44:34 +0100 Subject: [PATCH 223/360] CI: workaround mongodb dependency for messaging and clustering master jobs The zaqar devstack plugin has a workaround for the failing dependency for ubuntu version 22.04. Let's use this as the runner in those jobs. --- .github/workflows/functional-clustering.yaml | 2 +- .github/workflows/functional-messaging.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/functional-clustering.yaml b/.github/workflows/functional-clustering.yaml index 0dcca74e1b..0ea9051d32 100644 --- a/.github/workflows/functional-clustering.yaml +++ b/.github/workflows/functional-clustering.yaml @@ -12,7 +12,7 @@ jobs: matrix: name: ["master"] openstack_version: ["master"] - ubuntu_version: ["20.04"] + ubuntu_version: ["22.04"] include: - name: "zed" openstack_version: "stable/zed" diff --git a/.github/workflows/functional-messaging.yaml b/.github/workflows/functional-messaging.yaml index 1c812d84ad..b976975dcd 100644 --- a/.github/workflows/functional-messaging.yaml +++ b/.github/workflows/functional-messaging.yaml @@ -12,7 +12,7 @@ jobs: matrix: name: ["master"] openstack_version: ["master"] - ubuntu_version: ["20.04"] + ubuntu_version: ["22.04"] include: - name: "zed" openstack_version: "stable/zed" From a4e484914f83b8b04e387ca55a00b6e5f2440931 Mon Sep 17 00:00:00 2001 From: Emil Maruszczak <emil.maruszczak@thehutgroup.com> Date: Thu, 29 Dec 2022 16:59:10 +0100 Subject: [PATCH 224/360] Add Get operation --- .../openstack/identity/v3/limits_test.go | 7 ++++- openstack/identity/v3/limits/doc.go | 7 +++++ openstack/identity/v3/limits/requests.go | 7 +++++ openstack/identity/v3/limits/results.go | 22 +++++++++++++ .../identity/v3/limits/testing/fixtures.go | 31 +++++++++++++++++++ .../v3/limits/testing/requests_test.go | 10 ++++++ openstack/identity/v3/limits/urls.go | 4 +++ 7 files changed, 87 insertions(+), 1 deletion(-) diff --git a/acceptance/openstack/identity/v3/limits_test.go b/acceptance/openstack/identity/v3/limits_test.go index a988c0aec6..9796a6de87 100644 --- a/acceptance/openstack/identity/v3/limits_test.go +++ b/acceptance/openstack/identity/v3/limits_test.go @@ -41,7 +41,7 @@ func TestLimitsList(t *testing.T) { th.AssertNoErr(t, err) } -func TestCreateLimits(t *testing.T) { +func TestLimitsCRUD(t *testing.T) { // TODO: After https://github.com/gophercloud/gophercloud/issues/2290 is implemented // use registered limits API to create global registered limit and then overwrite it with limit. // Current solution (using glance limit) only works with Openstack Xena and above. @@ -97,4 +97,9 @@ func TestCreateLimits(t *testing.T) { th.AssertEquals(t, serviceID, createdLimits[0].ServiceID) th.AssertEquals(t, project.ID, createdLimits[0].ProjectID) + limitID := createdLimits[0].ID + + limit, err := limits.Get(client, limitID).Extract() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, createdLimits[0], *limit) } diff --git a/openstack/identity/v3/limits/doc.go b/openstack/identity/v3/limits/doc.go index ed02b478e4..57a257717a 100644 --- a/openstack/identity/v3/limits/doc.go +++ b/openstack/identity/v3/limits/doc.go @@ -48,5 +48,12 @@ Example to Create Limits if err != nil { panic(err) } + +Example to Get a Limit + + limit, err := limits.Get(identityClient, "25a04c7a065c430590881c646cdcdd58").Extract() + if err != nil { + panic(err) + } */ package limits diff --git a/openstack/identity/v3/limits/requests.go b/openstack/identity/v3/limits/requests.go index c6adeb002f..38b0975c56 100644 --- a/openstack/identity/v3/limits/requests.go +++ b/openstack/identity/v3/limits/requests.go @@ -119,3 +119,10 @@ func BatchCreate(client *gophercloud.ServiceClient, opts BatchCreateOptsBuilder) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } + +// Get retrieves details on a single limit, by ID. +func Get(client *gophercloud.ServiceClient, limitID string) (r GetResult) { + resp, err := client.Get(resourceURL(client, limitID), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} diff --git a/openstack/identity/v3/limits/results.go b/openstack/identity/v3/limits/results.go index ae48c64724..2f25edcb50 100644 --- a/openstack/identity/v3/limits/results.go +++ b/openstack/identity/v3/limits/results.go @@ -64,6 +64,11 @@ type LimitsOutput struct { Limits []Limit `json:"limits"` } +// A LimitOutput is an encapsulated Limit returned by Get and Update operations +type LimitOutput struct { + Limit *Limit `json:"limit"` +} + // LimitPage is a single page of Limit results. type LimitPage struct { pagination.LinkedPageBase @@ -75,6 +80,16 @@ type CreateResult struct { gophercloud.Result } +type commonResult struct { + gophercloud.Result +} + +// GetResult is the response from a Get operation. Call its Extract method +// to interpret it as a Limit. +type GetResult struct { + commonResult +} + // IsEmpty determines whether or not a page of Limits contains any results. func (r LimitPage) IsEmpty() (bool, error) { if r.StatusCode == 204 { @@ -114,3 +129,10 @@ func (r CreateResult) Extract() ([]Limit, error) { err := r.ExtractInto(&out) return out.Limits, err } + +// Extract interprets any commonResult as a Limit. +func (r commonResult) Extract() (*Limit, error) { + var out LimitOutput + err := r.ExtractInto(&out) + return out.Limit, err +} diff --git a/openstack/identity/v3/limits/testing/fixtures.go b/openstack/identity/v3/limits/testing/fixtures.go index 39afaca423..d3a7330952 100644 --- a/openstack/identity/v3/limits/testing/fixtures.go +++ b/openstack/identity/v3/limits/testing/fixtures.go @@ -79,6 +79,23 @@ const CreateRequest = ` } ` +const GetOutput = ` +{ + "limit": { + "resource_name": "volume", + "region_id": null, + "links": { + "self": "http://10.3.150.25/identity/v3/limits/25a04c7a065c430590881c646cdcdd58" + }, + "service_id": "9408080f1970482aa0e38bc2d4ea34b7", + "project_id": "3a705b9f56bb439381b43c4fe59dccce", + "id": "25a04c7a065c430590881c646cdcdd58", + "resource_limit": 11, + "description": "Number of volumes for project 3a705b9f56bb439381b43c4fe59dccce" + } +} +` + // Model is the enforcement model in the GetEnforcementModel request. var Model = limits.EnforcementModel{ Name: "flat", @@ -156,3 +173,17 @@ func HandleCreateLimitSuccessfully(t *testing.T) { fmt.Fprintf(w, CreateOutput) }) } + +// HandleGetLimitSuccessfully creates an HTTP handler at `/limits` on the +// test handler mux that responds with a single limit. +func HandleGetLimitSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/limits/25a04c7a065c430590881c646cdcdd58", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "Accept", "application/json") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, GetOutput) + }) +} diff --git a/openstack/identity/v3/limits/testing/requests_test.go b/openstack/identity/v3/limits/testing/requests_test.go index 1f7a2037f2..90d08ad13a 100644 --- a/openstack/identity/v3/limits/testing/requests_test.go +++ b/openstack/identity/v3/limits/testing/requests_test.go @@ -77,3 +77,13 @@ func TestCreateLimits(t *testing.T) { th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedLimitsSlice, actual) } + +func TestGetLimit(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleGetLimitSuccessfully(t) + + actual, err := limits.Get(client.ServiceClient(), "25a04c7a065c430590881c646cdcdd58").Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, FirstLimit, *actual) +} diff --git a/openstack/identity/v3/limits/urls.go b/openstack/identity/v3/limits/urls.go index 79498df3bd..ce8ca245e5 100644 --- a/openstack/identity/v3/limits/urls.go +++ b/openstack/identity/v3/limits/urls.go @@ -14,3 +14,7 @@ func enforcementModelURL(client *gophercloud.ServiceClient) string { func rootURL(client *gophercloud.ServiceClient) string { return client.ServiceURL(rootPath) } + +func resourceURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL(rootPath, id) +} From e855edc571441459d028acac4aa5f53dd10f2302 Mon Sep 17 00:00:00 2001 From: Pierre Prinetti <pierreprinetti@redhat.com> Date: Wed, 22 Mar 2023 18:34:09 +0100 Subject: [PATCH 225/360] objects: Add some validation to BulkDelete * Validate that the container name does not contain `/` * Validate that all provided objects are non-empty --- openstack/objectstorage/v1/objects/requests.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/openstack/objectstorage/v1/objects/requests.go b/openstack/objectstorage/v1/objects/requests.go index 1a1147a14d..60a02b5f21 100644 --- a/openstack/objectstorage/v1/objects/requests.go +++ b/openstack/objectstorage/v1/objects/requests.go @@ -576,11 +576,21 @@ func CreateTempURL(c *gophercloud.ServiceClient, containerName, objectName strin // BulkDelete is a function that bulk deletes objects. func BulkDelete(c *gophercloud.ServiceClient, container string, objects []string) (r BulkDeleteResult) { + err := containers.CheckContainerName(container) + if err != nil { + r.Err = err + return + } + // urlencode object names to be on the safe side // https://github.com/openstack/swift/blob/stable/train/swift/common/middleware/bulk.py#L160 // https://github.com/openstack/swift/blob/stable/train/swift/common/swob.py#L302 encodedObjects := make([]string, len(objects)) for i, v := range objects { + if v == "" { + r.Err = fmt.Errorf("object names must not be the empty string") + return + } encodedObjects[i] = strings.Join([]string{container, v}, "/") } b := strings.NewReader(strings.Join(encodedObjects, "\n") + "\n") From f282ec4929199bdeada577799a1db8cc4c388fab Mon Sep 17 00:00:00 2001 From: Pierre Prinetti <pierreprinetti@redhat.com> Date: Wed, 22 Mar 2023 18:35:01 +0100 Subject: [PATCH 226/360] objects: Add optimised BulkDelete function Use `bytes.Buffer` instead of repeatedly copying strings to compose the request body for `objects.BulkDelete`. This commit does not hook the function to the API; it provides the benchmark tests. Benchmark with: ```shell cd openstack/objectstorage/v1/objects/testing go test -bench=. ``` Locally I get a marginal gain when bulk-deleting only a few objects, and good gains (to around 3/4 of the time per execution) with large batches: ```plaintext goos: linux goarch: amd64 pkg: github.com/gophercloud/gophercloud/openstack/objectstorage/v1/objects/testing cpu: Intel(R) Core(TM) i7-8650U CPU @ 1.90GHz BenchmarkBulkDelete-8 246 4536244 ns/op BenchmarkBulkDeleteFewerObjects-8 11943 97338 ns/op BenchmarkBulkDeleteWithBuffer-8 303 3463102 ns/op BenchmarkBulkDeleteWithBufferFewerObjects-8 11587 92153 ns/op PASS ok github.com/gophercloud/gophercloud/openstack/objectstorage/v1/objects/testing 19.943s ``` --- .../objectstorage/v1/objects/requests.go | 31 +++++++ .../v1/objects/testing/fixtures.go | 10 +++ .../v1/objects/testing/requests_test.go | 81 +++++++++++++++++++ 3 files changed, 122 insertions(+) diff --git a/openstack/objectstorage/v1/objects/requests.go b/openstack/objectstorage/v1/objects/requests.go index 60a02b5f21..5fe0d061b5 100644 --- a/openstack/objectstorage/v1/objects/requests.go +++ b/openstack/objectstorage/v1/objects/requests.go @@ -604,3 +604,34 @@ func BulkDelete(c *gophercloud.ServiceClient, container string, objects []string _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } + +// BulkDeleteWithBuffer is a function that bulk deletes objects. +func BulkDeleteWithBuffer(c *gophercloud.ServiceClient, container string, objects []string) (r BulkDeleteResult) { + err := containers.CheckContainerName(container) + if err != nil { + r.Err = err + return + } + + var body bytes.Buffer + for i := range objects { + if objects[i] == "" { + r.Err = fmt.Errorf("object names must not be the empty string") + return + } + body.WriteString(container) + body.WriteRune('/') + body.WriteString(objects[i]) + body.WriteRune('\n') + } + + resp, err := c.Post(bulkDeleteURL(c), &body, &r.Body, &gophercloud.RequestOpts{ + MoreHeaders: map[string]string{ + "Accept": "application/json", + "Content-Type": "text/plain", + }, + OkCodes: []int{200}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} diff --git a/openstack/objectstorage/v1/objects/testing/fixtures.go b/openstack/objectstorage/v1/objects/testing/fixtures.go index d21dc74b6b..0d89cbb7c9 100644 --- a/openstack/objectstorage/v1/objects/testing/fixtures.go +++ b/openstack/objectstorage/v1/objects/testing/fixtures.go @@ -342,6 +342,16 @@ func HandleBulkDeleteSuccessfully(t *testing.T) { }) } +// HandleBulkDeleteBenchmark creates an HTTP handler at `/` on the test +// handler mux that does as little as possible. +func HandleBulkDeleteBenchmark(b *testing.B) { + th.Mux.HandleFunc("/", func(w http.ResponseWriter, _ *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, bulkDeleteResponse) + }) +} + // HandleUpdateObjectSuccessfully creates an HTTP handler at `/testContainer/testObject` on the test handler mux that // responds with a `Update` response. func HandleUpdateObjectSuccessfully(t *testing.T, options ...option) { diff --git a/openstack/objectstorage/v1/objects/testing/requests_test.go b/openstack/objectstorage/v1/objects/testing/requests_test.go index fdac08c211..5515d94a73 100644 --- a/openstack/objectstorage/v1/objects/testing/requests_test.go +++ b/openstack/objectstorage/v1/objects/testing/requests_test.go @@ -7,6 +7,7 @@ import ( "io" "io/ioutil" "net/http" + "strconv" "strings" "testing" "time" @@ -388,6 +389,86 @@ func TestBulkDelete(t *testing.T) { th.AssertDeepEquals(t, expected, *resp) } +func BenchmarkBulkDelete(b *testing.B) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleBulkDeleteBenchmark(b) + + objectNames := make([]string, 10000) + for i := range objectNames { + objectNames[i] = "a long, long name for very many objects that are not stored anywhere. Number " + strconv.Itoa(i) + "." + } + + serviceClient := fake.ServiceClient() + + b.ResetTimer() + for n := 0; n < b.N; n++ { + if res := objects.BulkDelete(serviceClient, "this is the container name", objectNames); res.Err != nil { + b.Errorf("unexpected error: %v", res.Err) + } + } +} + +func BenchmarkBulkDeleteFewerObjects(b *testing.B) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleBulkDeleteBenchmark(b) + + objectNames := make([]string, 10) + for i := range objectNames { + objectNames[i] = "a long, long name for very many objects that are not stored anywhere. Number " + strconv.Itoa(i) + "." + } + + serviceClient := fake.ServiceClient() + + b.ResetTimer() + for n := 0; n < b.N; n++ { + if res := objects.BulkDelete(serviceClient, "this is the container name", objectNames); res.Err != nil { + b.Errorf("unexpected error: %v", res.Err) + } + } +} + +func BenchmarkBulkDeleteWithBuffer(b *testing.B) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleBulkDeleteBenchmark(b) + + objectNames := make([]string, 10000) + for i := range objectNames { + objectNames[i] = "a long, long name for very many objects that are not stored anywhere. Number " + strconv.Itoa(i) + "." + } + + serviceClient := fake.ServiceClient() + + b.ResetTimer() + for n := 0; n < b.N; n++ { + if res := objects.BulkDeleteWithBuffer(serviceClient, "this is the container name", objectNames); res.Err != nil { + b.Errorf("unexpected error: %v", res.Err) + } + } +} + +func BenchmarkBulkDeleteWithBufferFewerObjects(b *testing.B) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleBulkDeleteBenchmark(b) + + objectNames := make([]string, 10) + for i := range objectNames { + objectNames[i] = "a long, long name for very many objects that are not stored anywhere. Number " + strconv.Itoa(i) + "." + } + + serviceClient := fake.ServiceClient() + + b.ResetTimer() + for n := 0; n < b.N; n++ { + if res := objects.BulkDeleteWithBuffer(serviceClient, "this is the container name", objectNames); res.Err != nil { + b.Errorf("unexpected error: %v", res.Err) + } + } +} + func TestUpateObjectMetadata(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() From 8c9fc9e37ca05ec3ed07f2d8d1080914a875eeaf Mon Sep 17 00:00:00 2001 From: Pierre Prinetti <pierreprinetti@redhat.com> Date: Tue, 28 Mar 2023 11:55:58 +0200 Subject: [PATCH 227/360] RELEASE.md: List PRs from last release Stop relying on milestones. A release process based on milestones is prone to error (because not all PRs were necessarily added to the right milestone), and moreover it is not applicable to the cherry-pick workflow. Instead, this new release process uses `gh` to retrieve all PRs since a given release. --- RELEASE.md | 53 +++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 37 insertions(+), 16 deletions(-) diff --git a/RELEASE.md b/RELEASE.md index b2937448b6..6490ed8877 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -15,36 +15,57 @@ Automation prevents merges if the label is not present. ### Metadata -The release notes for a given release are generated based on the PR title and its milestone: -* make sure that the PR title is descriptive -* add a milestone based on the semver label: x++ if major, y++ if minor, z++ if patch. +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 -### Step 1: Check the metadata +Requirements: +* [`gh`](https://github.com/cli/cli) +* [`jq`](https://stedolan.github.io/jq/) -Check that all pull requests merged since the last release have the right milestone. +### Step 1: Collect all PRs since the last release -### Step 2: Release notes and version string +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: -Once all PRs have a sensible title and are added to the right milestone, generate the release notes with the [`gh`](https://github.com/cli/cli) tool: ```shell -gh pr list \ - --state merged \ - --search 'milestone:vx.y.z' \ - --json number,title \ - --template \ - '{{range .}}* {{ printf "[GH-%v](https://github.com/gophercloud/gophercloud/pull/%v)" .number .number }} {{ .title }} -{{end}}' +gorelease | grep -B2 -A0 '^## incompatible changes' ``` -Replace `x.y.z` with the current milestone. +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, and the milestone corresponding to its version. +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 From 72205202bfeae679f113e0354bbca444b74d8449 Mon Sep 17 00:00:00 2001 From: Pierre Prinetti <pierreprinetti@redhat.com> Date: Tue, 28 Mar 2023 13:59:03 +0200 Subject: [PATCH 228/360] Prepare v1.3.0 --- CHANGELOG.md | 31 +++++++++++++++++++++++++++++++ provider_client.go | 2 +- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 63a956bf19..9e2567b98b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,34 @@ +## 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 diff --git a/provider_client.go b/provider_client.go index e6e80258ec..c603d6dbe3 100644 --- a/provider_client.go +++ b/provider_client.go @@ -14,7 +14,7 @@ import ( // DefaultUserAgent is the default User-Agent string set in the request header. const ( - DefaultUserAgent = "gophercloud/v1.2.0" + DefaultUserAgent = "gophercloud/v1.3.0" DefaultMaxBackoffRetries = 60 ) From 330f72eade1dc9874aff5cb532643889e6d4a5f3 Mon Sep 17 00:00:00 2001 From: kayrus <kay.diam@gmail.com> Date: Thu, 30 Mar 2023 11:50:04 +0200 Subject: [PATCH 229/360] [swift v1]: Add TempURLKey and Digest arguments for objects.CreateTempURLOpts --- .../objectstorage/v1/objects_test.go | 67 ++++++++++---- .../objectstorage/v1/objects/requests.go | 92 +++++++++++++++---- .../v1/objects/testing/requests_test.go | 10 +- 3 files changed, 132 insertions(+), 37 deletions(-) diff --git a/acceptance/openstack/objectstorage/v1/objects_test.go b/acceptance/openstack/objectstorage/v1/objects_test.go index 384a2d3b37..0569428f3d 100644 --- a/acceptance/openstack/objectstorage/v1/objects_test.go +++ b/acceptance/openstack/objectstorage/v1/objects_test.go @@ -4,11 +4,12 @@ package v1 import ( - "bytes" + "fmt" "io/ioutil" "net/http" "strings" "testing" + "time" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" @@ -34,7 +35,10 @@ func TestObjects(t *testing.T) { // Create a container to hold the test objects. cName := tools.RandomString("test-container-", 8) - header, err := containers.Create(client, cName, nil).Extract() + opts := containers.CreateOpts{ + TempURLKey: "super-secret", + } + header, err := containers.Create(client, cName, opts).Extract() th.AssertNoErr(t, err) t.Logf("Create object headers: %+v\n", header) @@ -45,11 +49,11 @@ func TestObjects(t *testing.T) { }() // Create a slice of buffers to hold the test object content. - oContents := make([]*bytes.Buffer, numObjects) + oContents := make([]string, numObjects) for i := 0; i < numObjects; i++ { - oContents[i] = bytes.NewBuffer([]byte(tools.RandomString("", 10))) + oContents[i] = tools.RandomString("", 10) createOpts := objects.CreateOpts{ - Content: oContents[i], + Content: strings.NewReader(oContents[i]), } res := objects.Create(client, cName, oNames[i], createOpts) th.AssertNoErr(t, res.Err) @@ -95,12 +99,37 @@ func TestObjects(t *testing.T) { }) th.AssertNoErr(t, err) - resp, err := http.Get(objURLs[i]) + resp, err := client.ProviderClient.HTTPClient.Get(objURLs[i]) th.AssertNoErr(t, err) + if resp.StatusCode != http.StatusOK { + resp.Body.Close() + th.AssertNoErr(t, fmt.Errorf("unexpected response code: %d", resp.StatusCode)) + } body, err := ioutil.ReadAll(resp.Body) th.AssertNoErr(t, err) - th.AssertDeepEquals(t, oContents[i].Bytes(), body) + th.AssertDeepEquals(t, oContents[i], string(body)) + resp.Body.Close() + + // custom Temp URL key with a sha256 digest and exact timestamp + objURLs[i], err = objects.CreateTempURL(client, cName, oNames[i], objects.CreateTempURLOpts{ + Method: http.MethodGet, + Timestamp: time.Now().UTC().Add(180 * time.Second), + Digest: "sha256", + TempURLKey: opts.TempURLKey, + }) + th.AssertNoErr(t, err) + + resp, err = client.ProviderClient.HTTPClient.Get(objURLs[i]) + th.AssertNoErr(t, err) + if resp.StatusCode != http.StatusOK { + resp.Body.Close() + th.AssertNoErr(t, fmt.Errorf("unexpected response code: %d", resp.StatusCode)) + } + + body, err = ioutil.ReadAll(resp.Body) + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, oContents[i], string(body)) resp.Body.Close() } @@ -235,11 +264,11 @@ func TestObjectsListSubdir(t *testing.T) { }() // Create a slice of buffers to hold the test object content. - oContents1 := make([]*bytes.Buffer, numObjects) + oContents1 := make([]string, numObjects) for i := 0; i < numObjects; i++ { - oContents1[i] = bytes.NewBuffer([]byte(tools.RandomString("", 10))) + oContents1[i] = tools.RandomString("", 10) createOpts := objects.CreateOpts{ - Content: oContents1[i], + Content: strings.NewReader(oContents1[i]), } res := objects.Create(client, cName, oNames1[i], createOpts) th.AssertNoErr(t, res.Err) @@ -253,11 +282,11 @@ func TestObjectsListSubdir(t *testing.T) { } }() - oContents2 := make([]*bytes.Buffer, numObjects) + oContents2 := make([]string, numObjects) for i := 0; i < numObjects; i++ { - oContents2[i] = bytes.NewBuffer([]byte(tools.RandomString("", 10))) + oContents2[i] = tools.RandomString("", 10) createOpts := objects.CreateOpts{ - Content: oContents2[i], + Content: strings.NewReader(oContents2[i]), } res := objects.Create(client, cName, oNames2[i], createOpts) th.AssertNoErr(t, res.Err) @@ -354,21 +383,21 @@ func TestObjectsBulkDelete(t *testing.T) { }() // Create a slice of buffers to hold the test object content. - oContents1 := make([]*bytes.Buffer, numObjects) + oContents1 := make([]string, numObjects) for i := 0; i < numObjects; i++ { - oContents1[i] = bytes.NewBuffer([]byte(tools.RandomString("", 10))) + oContents1[i] = tools.RandomString("", 10) createOpts := objects.CreateOpts{ - Content: oContents1[i], + Content: strings.NewReader(oContents1[i]), } res := objects.Create(client, cName, oNames1[i], createOpts) th.AssertNoErr(t, res.Err) } - oContents2 := make([]*bytes.Buffer, numObjects) + oContents2 := make([]string, numObjects) for i := 0; i < numObjects; i++ { - oContents2[i] = bytes.NewBuffer([]byte(tools.RandomString("", 10))) + oContents2[i] = tools.RandomString("", 10) createOpts := objects.CreateOpts{ - Content: oContents2[i], + Content: strings.NewReader(oContents2[i]), } res := objects.Create(client, cName, oNames2[i], createOpts) th.AssertNoErr(t, res.Err) diff --git a/openstack/objectstorage/v1/objects/requests.go b/openstack/objectstorage/v1/objects/requests.go index 1a1147a14d..45aad4cf63 100644 --- a/openstack/objectstorage/v1/objects/requests.go +++ b/openstack/objectstorage/v1/objects/requests.go @@ -5,7 +5,10 @@ import ( "crypto/hmac" "crypto/md5" "crypto/sha1" + "crypto/sha256" + "crypto/sha512" "fmt" + "hash" "io" "io/ioutil" "strings" @@ -17,6 +20,25 @@ import ( "github.com/gophercloud/gophercloud/pagination" ) +// ErrTempURLKeyNotFound is an error indicating that the Temp URL key was +// neigther set nor resolved from a container or account metadata. +type ErrTempURLKeyNotFound struct{ gophercloud.ErrMissingInput } + +func (e ErrTempURLKeyNotFound) Error() string { + return "Unable to obtain the Temp URL key." +} + +// ErrTempURLDigestNotValid is an error indicating that the requested +// cryptographic hash function is not supported. +type ErrTempURLDigestNotValid struct { + gophercloud.ErrMissingInput + Digest string +} + +func (e ErrTempURLDigestNotValid) Error() string { + return fmt.Sprintf("The requested %q digest is not supported.", e.Digest) +} + // ListOptsBuilder allows extensions to add additional parameters to the List // request. type ListOptsBuilder interface { @@ -504,15 +526,20 @@ type HTTPMethod string var ( // GET represents an HTTP "GET" method. GET HTTPMethod = "GET" - + // HEAD represents an HTTP "HEAD" method. + HEAD HTTPMethod = "HEAD" + // PUT represents an HTTP "PUT" method. + PUT HTTPMethod = "PUT" // POST represents an HTTP "POST" method. POST HTTPMethod = "POST" + // DELETE represents an HTTP "DELETE" method. + DELETE HTTPMethod = "DELETE" ) // CreateTempURLOpts are options for creating a temporary URL for an object. type CreateTempURLOpts struct { // (REQUIRED) Method is the HTTP method to allow for users of the temp URL. - // Valid values are "GET" and "POST". + // Valid values are "GET", "HEAD", "PUT", "POST" and "DELETE". Method HTTPMethod // (REQUIRED) TTL is the number of seconds the temp URL should be active. @@ -523,8 +550,21 @@ type CreateTempURLOpts struct { // empty, the default OpenStack URL split point will be used ("/v1/"). Split string - // Timestamp is a timestamp to calculate Temp URL signature. Optional. + // (Optional) Timestamp is the current timestamp used to calculate the Temp URL + // signature. If not specified, the current UNIX timestamp is used as the base + // timestamp. Timestamp time.Time + + // (Optional) TempURLKey overrides the Swift container or account Temp URL key. + // TempURLKey must correspond to a target container/account key, otherwise the + // generated link will be invalid. If not specified, the key is obtained from + // a Swift container or account. + TempURLKey string + + // (Optional) Digest specifies the cryptographic hash function used to + // calculate the signature. Valid values include sha1, sha256, and + // sha512. If not specified, the default hash function is sha1. + Digest string } // CreateTempURL is a function for creating a temporary URL for an object. It @@ -541,34 +581,52 @@ func CreateTempURL(c *gophercloud.ServiceClient, containerName, objectName strin } // Initialize time if it was not passed as opts - var date time.Time - if opts.Timestamp.IsZero() { - date = time.Now().UTC() - } else { - date = opts.Timestamp + date := opts.Timestamp + if date.IsZero() { + date = time.Now() } - duration := time.Duration(opts.TTL) * time.Second + // UNIX time is always UTC expiry := date.Add(duration).Unix() - getHeader, err := containers.Get(c, containerName, nil).Extract() - if err != nil { - return "", err - } - tempURLKey := getHeader.TempURLKey + + // Initialize the tempURLKey to calculate a signature + tempURLKey := opts.TempURLKey if tempURLKey == "" { - // fallback to an account TempURL key - getHeader, err := accounts.Get(c, nil).Extract() + // fallback to a container TempURL key + getHeader, err := containers.Get(c, containerName, nil).Extract() if err != nil { return "", err } tempURLKey = getHeader.TempURLKey + if tempURLKey == "" { + // fallback to an account TempURL key + getHeader, err := accounts.Get(c, nil).Extract() + if err != nil { + return "", err + } + tempURLKey = getHeader.TempURLKey + } + if tempURLKey == "" { + return "", ErrTempURLKeyNotFound{} + } } + secretKey := []byte(tempURLKey) splitPath := strings.Split(url, opts.Split) baseURL, objectPath := splitPath[0], splitPath[1] objectPath = opts.Split + objectPath body := fmt.Sprintf("%s\n%d\n%s", opts.Method, expiry, objectPath) - hash := hmac.New(sha1.New, secretKey) + var hash hash.Hash + switch opts.Digest { + case "", "sha1": + hash = hmac.New(sha1.New, secretKey) + case "sha256": + hash = hmac.New(sha256.New, secretKey) + case "sha512": + hash = hmac.New(sha512.New, secretKey) + default: + return "", ErrTempURLDigestNotValid{Digest: opts.Digest} + } hash.Write([]byte(body)) hexsum := fmt.Sprintf("%x", hash.Sum(nil)) return fmt.Sprintf("%s%s?temp_url_sig=%s&temp_url_expires=%d", baseURL, objectPath, hexsum, expiry), nil diff --git a/openstack/objectstorage/v1/objects/testing/requests_test.go b/openstack/objectstorage/v1/objects/testing/requests_test.go index fdac08c211..11e2235f8a 100644 --- a/openstack/objectstorage/v1/objects/testing/requests_test.go +++ b/openstack/objectstorage/v1/objects/testing/requests_test.go @@ -454,7 +454,7 @@ func TestETag(t *testing.T) { func TestObjectCreateParamsWithoutSeek(t *testing.T) { content := "I do not implement Seek()" - buf := bytes.NewBuffer([]byte(content)) + buf := strings.NewReader(content) createOpts := objects.CreateOpts{Content: buf} reader, headers, _, err := createOpts.ToObjectCreateParams() @@ -517,4 +517,12 @@ func TestCreateTempURL(t *testing.T) { th.AssertNoErr(t, err) th.AssertEquals(t, expectedURL, tempURL) + + // Test TTL=0, but different timestamp + tempURL, err = objects.CreateTempURL(client, "testContainer", "testObject/testFile.txt", objects.CreateTempURLOpts{ + Method: http.MethodGet, + Timestamp: time.Date(2020, 07, 01, 01, 13, 00, 00, time.UTC), + }) + th.AssertNoErr(t, err) + th.AssertEquals(t, expectedURL, tempURL) } From b51908fb61c1e8ecfff2ffb834949b16cbbceb8c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Apr 2023 10:03:20 +0000 Subject: [PATCH 230/360] Bump mheap/github-action-required-labels from 3 to 4 Bumps [mheap/github-action-required-labels](https://github.com/mheap/github-action-required-labels) from 3 to 4. - [Release notes](https://github.com/mheap/github-action-required-labels/releases) - [Commits](https://github.com/mheap/github-action-required-labels/compare/v3...v4) --- updated-dependencies: - dependency-name: mheap/github-action-required-labels dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> --- .github/workflows/semver-require.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/semver-require.yaml b/.github/workflows/semver-require.yaml index ee78d014d5..99083ecce1 100644 --- a/.github/workflows/semver-require.yaml +++ b/.github/workflows/semver-require.yaml @@ -10,7 +10,7 @@ jobs: semver: runs-on: ubuntu-latest steps: - - uses: mheap/github-action-required-labels@v3 + - uses: mheap/github-action-required-labels@v4 with: mode: exactly count: 1 From 58b1c672da05790906bfc6e764f2c4bfc2cb8b76 Mon Sep 17 00:00:00 2001 From: Pierre Prinetti <pierreprinetti@redhat.com> Date: Wed, 22 Mar 2023 18:41:25 +0100 Subject: [PATCH 231/360] objects: Cleanup, remove benchmarks This commit replaces the old BulkDelete function with the new one, which has been measured to be faster. --- .../objectstorage/v1/objects/requests.go | 36 ++------- .../v1/objects/testing/fixtures.go | 10 --- .../v1/objects/testing/requests_test.go | 81 ------------------- 3 files changed, 5 insertions(+), 122 deletions(-) diff --git a/openstack/objectstorage/v1/objects/requests.go b/openstack/objectstorage/v1/objects/requests.go index 5fe0d061b5..0a1eb1cb40 100644 --- a/openstack/objectstorage/v1/objects/requests.go +++ b/openstack/objectstorage/v1/objects/requests.go @@ -575,6 +575,11 @@ func CreateTempURL(c *gophercloud.ServiceClient, containerName, objectName strin } // BulkDelete is a function that bulk deletes objects. +// In Swift, the maximum number of deletes per request is set by default to 10000. +// +// See: +// * https://github.com/openstack/swift/blob/6d3d4197151f44bf28b51257c1a4c5d33411dcae/etc/proxy-server.conf-sample#L1029-L1034 +// * https://github.com/openstack/swift/blob/e8cecf7fcc1630ee83b08f9a73e1e59c07f8d372/swift/common/middleware/bulk.py#L309 func BulkDelete(c *gophercloud.ServiceClient, container string, objects []string) (r BulkDeleteResult) { err := containers.CheckContainerName(container) if err != nil { @@ -582,37 +587,6 @@ func BulkDelete(c *gophercloud.ServiceClient, container string, objects []string return } - // urlencode object names to be on the safe side - // https://github.com/openstack/swift/blob/stable/train/swift/common/middleware/bulk.py#L160 - // https://github.com/openstack/swift/blob/stable/train/swift/common/swob.py#L302 - encodedObjects := make([]string, len(objects)) - for i, v := range objects { - if v == "" { - r.Err = fmt.Errorf("object names must not be the empty string") - return - } - encodedObjects[i] = strings.Join([]string{container, v}, "/") - } - b := strings.NewReader(strings.Join(encodedObjects, "\n") + "\n") - resp, err := c.Post(bulkDeleteURL(c), b, &r.Body, &gophercloud.RequestOpts{ - MoreHeaders: map[string]string{ - "Accept": "application/json", - "Content-Type": "text/plain", - }, - OkCodes: []int{200}, - }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// BulkDeleteWithBuffer is a function that bulk deletes objects. -func BulkDeleteWithBuffer(c *gophercloud.ServiceClient, container string, objects []string) (r BulkDeleteResult) { - err := containers.CheckContainerName(container) - if err != nil { - r.Err = err - return - } - var body bytes.Buffer for i := range objects { if objects[i] == "" { diff --git a/openstack/objectstorage/v1/objects/testing/fixtures.go b/openstack/objectstorage/v1/objects/testing/fixtures.go index 0d89cbb7c9..d21dc74b6b 100644 --- a/openstack/objectstorage/v1/objects/testing/fixtures.go +++ b/openstack/objectstorage/v1/objects/testing/fixtures.go @@ -342,16 +342,6 @@ func HandleBulkDeleteSuccessfully(t *testing.T) { }) } -// HandleBulkDeleteBenchmark creates an HTTP handler at `/` on the test -// handler mux that does as little as possible. -func HandleBulkDeleteBenchmark(b *testing.B) { - th.Mux.HandleFunc("/", func(w http.ResponseWriter, _ *http.Request) { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, bulkDeleteResponse) - }) -} - // HandleUpdateObjectSuccessfully creates an HTTP handler at `/testContainer/testObject` on the test handler mux that // responds with a `Update` response. func HandleUpdateObjectSuccessfully(t *testing.T, options ...option) { diff --git a/openstack/objectstorage/v1/objects/testing/requests_test.go b/openstack/objectstorage/v1/objects/testing/requests_test.go index 5515d94a73..fdac08c211 100644 --- a/openstack/objectstorage/v1/objects/testing/requests_test.go +++ b/openstack/objectstorage/v1/objects/testing/requests_test.go @@ -7,7 +7,6 @@ import ( "io" "io/ioutil" "net/http" - "strconv" "strings" "testing" "time" @@ -389,86 +388,6 @@ func TestBulkDelete(t *testing.T) { th.AssertDeepEquals(t, expected, *resp) } -func BenchmarkBulkDelete(b *testing.B) { - th.SetupHTTP() - defer th.TeardownHTTP() - HandleBulkDeleteBenchmark(b) - - objectNames := make([]string, 10000) - for i := range objectNames { - objectNames[i] = "a long, long name for very many objects that are not stored anywhere. Number " + strconv.Itoa(i) + "." - } - - serviceClient := fake.ServiceClient() - - b.ResetTimer() - for n := 0; n < b.N; n++ { - if res := objects.BulkDelete(serviceClient, "this is the container name", objectNames); res.Err != nil { - b.Errorf("unexpected error: %v", res.Err) - } - } -} - -func BenchmarkBulkDeleteFewerObjects(b *testing.B) { - th.SetupHTTP() - defer th.TeardownHTTP() - HandleBulkDeleteBenchmark(b) - - objectNames := make([]string, 10) - for i := range objectNames { - objectNames[i] = "a long, long name for very many objects that are not stored anywhere. Number " + strconv.Itoa(i) + "." - } - - serviceClient := fake.ServiceClient() - - b.ResetTimer() - for n := 0; n < b.N; n++ { - if res := objects.BulkDelete(serviceClient, "this is the container name", objectNames); res.Err != nil { - b.Errorf("unexpected error: %v", res.Err) - } - } -} - -func BenchmarkBulkDeleteWithBuffer(b *testing.B) { - th.SetupHTTP() - defer th.TeardownHTTP() - HandleBulkDeleteBenchmark(b) - - objectNames := make([]string, 10000) - for i := range objectNames { - objectNames[i] = "a long, long name for very many objects that are not stored anywhere. Number " + strconv.Itoa(i) + "." - } - - serviceClient := fake.ServiceClient() - - b.ResetTimer() - for n := 0; n < b.N; n++ { - if res := objects.BulkDeleteWithBuffer(serviceClient, "this is the container name", objectNames); res.Err != nil { - b.Errorf("unexpected error: %v", res.Err) - } - } -} - -func BenchmarkBulkDeleteWithBufferFewerObjects(b *testing.B) { - th.SetupHTTP() - defer th.TeardownHTTP() - HandleBulkDeleteBenchmark(b) - - objectNames := make([]string, 10) - for i := range objectNames { - objectNames[i] = "a long, long name for very many objects that are not stored anywhere. Number " + strconv.Itoa(i) + "." - } - - serviceClient := fake.ServiceClient() - - b.ResetTimer() - for n := 0; n < b.N; n++ { - if res := objects.BulkDeleteWithBuffer(serviceClient, "this is the container name", objectNames); res.Err != nil { - b.Errorf("unexpected error: %v", res.Err) - } - } -} - func TestUpateObjectMetadata(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() From 3afba9de97c3dbac476ad745fb47b7cf1c297abd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= <martin.andre@gmail.com> Date: Sun, 9 Apr 2023 21:47:35 +0200 Subject: [PATCH 232/360] Drop train and ussuri jobs The Ubuntu 18.04 image for github action runner was deprecated [1]. Ubuntu is is unfortunately the only linux image available as a github action runner and using a more recent version of ubuntu to run the train and ussuri jobs proved to be impracticable [2]. We're forced to retire those jobs. [1] https://github.blog/changelog/2022-08-09-github-actions-the-ubuntu-18-04-actions-runner-image-is-being-deprecated-and-will-be-removed-by-12-1-22/ [2] https://github.com/gophercloud/gophercloud/pull/2585 --- .github/workflows/functional-baremetal.yaml | 3 --- .github/workflows/functional-basic.yaml | 6 ------ .github/workflows/functional-blockstorage.yaml | 6 ------ .github/workflows/functional-clustering.yaml | 6 ------ .github/workflows/functional-compute.yaml | 6 ------ .github/workflows/functional-containerinfra.yaml | 10 ---------- .github/workflows/functional-dns.yaml | 10 ---------- .github/workflows/functional-identity.yaml | 6 ------ .github/workflows/functional-imageservice.yaml | 6 ------ .github/workflows/functional-keymanager.yaml | 6 ------ .github/workflows/functional-loadbalancer.yaml | 6 ------ .github/workflows/functional-messaging.yaml | 6 ------ .github/workflows/functional-networking.yaml | 14 -------------- .github/workflows/functional-objectstorage.yaml | 6 ------ .github/workflows/functional-orchestration.yaml | 6 ------ .github/workflows/functional-placement.yaml | 6 ------ .../workflows/functional-sharedfilesystems.yaml | 6 ------ 17 files changed, 115 deletions(-) diff --git a/.github/workflows/functional-baremetal.yaml b/.github/workflows/functional-baremetal.yaml index f7b1bccde5..0ae2f53a29 100644 --- a/.github/workflows/functional-baremetal.yaml +++ b/.github/workflows/functional-baremetal.yaml @@ -29,9 +29,6 @@ jobs: - name: "victoria" openstack_version: "stable/victoria" ubuntu_version: "20.04" - - name: "ussuri" - openstack_version: "stable/ussuri" - ubuntu_version: "18.04" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Deploy OpenStack ${{ matrix.name }} with Ironic and run baremetal acceptance tests steps: diff --git a/.github/workflows/functional-basic.yaml b/.github/workflows/functional-basic.yaml index 66cf65c854..d013fe11c8 100644 --- a/.github/workflows/functional-basic.yaml +++ b/.github/workflows/functional-basic.yaml @@ -32,12 +32,6 @@ jobs: - name: "victoria" openstack_version: "stable/victoria" ubuntu_version: "20.04" - - name: "ussuri" - openstack_version: "stable/ussuri" - ubuntu_version: "18.04" - - name: "train" - openstack_version: "stable/train" - ubuntu_version: "18.04" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Deploy OpenStack ${{ matrix.name }} with defaults and run basic acceptance tests steps: diff --git a/.github/workflows/functional-blockstorage.yaml b/.github/workflows/functional-blockstorage.yaml index 296f5ffc19..07624e7cb1 100644 --- a/.github/workflows/functional-blockstorage.yaml +++ b/.github/workflows/functional-blockstorage.yaml @@ -29,12 +29,6 @@ jobs: - name: "victoria" openstack_version: "stable/victoria" ubuntu_version: "20.04" - - name: "ussuri" - openstack_version: "stable/ussuri" - ubuntu_version: "18.04" - - name: "train" - openstack_version: "stable/train" - ubuntu_version: "18.04" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Deploy OpenStack ${{ matrix.name }} with Cinder and run blockstorage acceptance tests steps: diff --git a/.github/workflows/functional-clustering.yaml b/.github/workflows/functional-clustering.yaml index 9a520aa15b..8c0eda5d6d 100644 --- a/.github/workflows/functional-clustering.yaml +++ b/.github/workflows/functional-clustering.yaml @@ -29,12 +29,6 @@ jobs: - name: "victoria" openstack_version: "stable/victoria" ubuntu_version: "20.04" - - name: "ussuri" - openstack_version: "stable/ussuri" - ubuntu_version: "18.04" - - name: "train" - openstack_version: "stable/train" - ubuntu_version: "18.04" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Deploy OpenStack ${{ matrix.name }} with Senlin and run clustering acceptance tests steps: diff --git a/.github/workflows/functional-compute.yaml b/.github/workflows/functional-compute.yaml index dfe9b39475..9085d04664 100644 --- a/.github/workflows/functional-compute.yaml +++ b/.github/workflows/functional-compute.yaml @@ -29,12 +29,6 @@ jobs: - name: "victoria" openstack_version: "stable/victoria" ubuntu_version: "20.04" - - name: "ussuri" - openstack_version: "stable/ussuri" - ubuntu_version: "18.04" - - name: "train" - openstack_version: "stable/train" - ubuntu_version: "18.04" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Deploy OpenStack ${{ matrix.name }} with Nova and run compute acceptance tests steps: diff --git a/.github/workflows/functional-containerinfra.yaml b/.github/workflows/functional-containerinfra.yaml index 9031262ada..3f3132f96d 100644 --- a/.github/workflows/functional-containerinfra.yaml +++ b/.github/workflows/functional-containerinfra.yaml @@ -41,16 +41,6 @@ jobs: ubuntu_version: "20.04" devstack_conf_overrides: | enable_plugin magnum https://github.com/openstack/magnum stable/victoria - - name: "ussuri" - openstack_version: "stable/ussuri" - ubuntu_version: "18.04" - devstack_conf_overrides: | - enable_plugin magnum https://github.com/openstack/magnum ussuri-eol - - name: "train" - openstack_version: "stable/train" - ubuntu_version: "18.04" - devstack_conf_overrides: | - enable_plugin magnum https://github.com/openstack/magnum train-eol runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Deploy OpenStack ${{ matrix.name }} with Magnum and run containerinfra acceptance tests steps: diff --git a/.github/workflows/functional-dns.yaml b/.github/workflows/functional-dns.yaml index b7b5d4eae9..bf5164d370 100644 --- a/.github/workflows/functional-dns.yaml +++ b/.github/workflows/functional-dns.yaml @@ -42,16 +42,6 @@ jobs: ubuntu_version: "20.04" devstack_conf_overrides: | enable_plugin designate https://github.com/openstack/designate stable/victoria - - name: "ussuri" - openstack_version: "stable/ussuri" - ubuntu_version: "18.04" - devstack_conf_overrides: | - enable_plugin designate https://github.com/openstack/designate stable/ussuri - - name: "train" - openstack_version: "stable/train" - ubuntu_version: "18.04" - devstack_conf_overrides: | - enable_plugin designate https://github.com/openstack/designate train-eol runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Deploy OpenStack ${{ matrix.name }} with Designate and run dns acceptance tests steps: diff --git a/.github/workflows/functional-identity.yaml b/.github/workflows/functional-identity.yaml index 40b3f71f25..478ac30a84 100644 --- a/.github/workflows/functional-identity.yaml +++ b/.github/workflows/functional-identity.yaml @@ -29,12 +29,6 @@ jobs: - name: "victoria" openstack_version: "stable/victoria" ubuntu_version: "20.04" - - name: "ussuri" - openstack_version: "stable/ussuri" - ubuntu_version: "18.04" - - name: "train" - openstack_version: "stable/train" - ubuntu_version: "18.04" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Deploy OpenStack ${{ matrix.name }} with Keystone and run identity acceptance tests steps: diff --git a/.github/workflows/functional-imageservice.yaml b/.github/workflows/functional-imageservice.yaml index 392811f80d..b60802b9d5 100644 --- a/.github/workflows/functional-imageservice.yaml +++ b/.github/workflows/functional-imageservice.yaml @@ -29,12 +29,6 @@ jobs: - name: "victoria" openstack_version: "stable/victoria" ubuntu_version: "20.04" - - name: "ussuri" - openstack_version: "stable/ussuri" - ubuntu_version: "18.04" - - name: "train" - openstack_version: "stable/train" - ubuntu_version: "18.04" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Deploy OpenStack ${{ matrix.name }} with Glance and run imageservice acceptance tests steps: diff --git a/.github/workflows/functional-keymanager.yaml b/.github/workflows/functional-keymanager.yaml index 9e79af6bf4..932de23c1b 100644 --- a/.github/workflows/functional-keymanager.yaml +++ b/.github/workflows/functional-keymanager.yaml @@ -29,12 +29,6 @@ jobs: - name: "victoria" openstack_version: "stable/victoria" ubuntu_version: "20.04" - - name: "ussuri" - openstack_version: "stable/ussuri" - ubuntu_version: "18.04" - - name: "train" - openstack_version: "stable/train" - ubuntu_version: "18.04" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Deploy OpenStack ${{ matrix.name }} with Barbican and run keymanager acceptance tests steps: diff --git a/.github/workflows/functional-loadbalancer.yaml b/.github/workflows/functional-loadbalancer.yaml index 7b764b323c..843f3645c5 100644 --- a/.github/workflows/functional-loadbalancer.yaml +++ b/.github/workflows/functional-loadbalancer.yaml @@ -29,12 +29,6 @@ jobs: - name: "victoria" openstack_version: "stable/victoria" ubuntu_version: "20.04" - - name: "ussuri" - openstack_version: "stable/ussuri" - ubuntu_version: "18.04" - - name: "train" - openstack_version: "stable/train" - ubuntu_version: "18.04" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Deploy OpenStack ${{ matrix.name }} with Octavia and run loadbalancer acceptance tests steps: diff --git a/.github/workflows/functional-messaging.yaml b/.github/workflows/functional-messaging.yaml index 3de0a9a1f3..3ab2f58f7d 100644 --- a/.github/workflows/functional-messaging.yaml +++ b/.github/workflows/functional-messaging.yaml @@ -29,12 +29,6 @@ jobs: - name: "victoria" openstack_version: "stable/victoria" ubuntu_version: "20.04" - - name: "ussuri" - openstack_version: "stable/ussuri" - ubuntu_version: "18.04" - - name: "train" - openstack_version: "stable/train" - ubuntu_version: "18.04" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Deploy OpenStack ${{ matrix.name }} with Zaqar and run messaging acceptance tests steps: diff --git a/.github/workflows/functional-networking.yaml b/.github/workflows/functional-networking.yaml index c6543bb867..461acdff16 100644 --- a/.github/workflows/functional-networking.yaml +++ b/.github/workflows/functional-networking.yaml @@ -47,20 +47,6 @@ jobs: devstack_conf_overrides: | enable_plugin neutron-dynamic-routing https://github.com/openstack/neutron-dynamic-routing stable/victoria enable_plugin neutron-vpnaas https://github.com/openstack/neutron-vpnaas stable/victoria - - name: "ussuri" - openstack_version: "stable/ussuri" - ubuntu_version: "18.04" - devstack_conf_overrides: | - enable_plugin neutron-fwaas https://github.com/openstack/neutron-fwaas stable/ussuri - enable_plugin neutron-dynamic-routing https://github.com/openstack/neutron-dynamic-routing stable/ussuri - enable_plugin neutron-vpnaas https://github.com/openstack/neutron-vpnaas ussuri-eol - - name: "train" - openstack_version: "stable/train" - ubuntu_version: "18.04" - devstack_conf_overrides: | - enable_plugin neutron-fwaas https://github.com/openstack/neutron-fwaas stable/train - enable_plugin neutron-dynamic-routing https://github.com/openstack/neutron-dynamic-routing train-eol - enable_plugin neutron-vpnaas https://github.com/openstack/neutron-vpnaas train-eol runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Deploy OpenStack ${{ matrix.name }} with Neutron and run networking acceptance tests steps: diff --git a/.github/workflows/functional-objectstorage.yaml b/.github/workflows/functional-objectstorage.yaml index a9dc003bc7..424daacea5 100644 --- a/.github/workflows/functional-objectstorage.yaml +++ b/.github/workflows/functional-objectstorage.yaml @@ -29,12 +29,6 @@ jobs: - name: "victoria" openstack_version: "stable/victoria" ubuntu_version: "20.04" - - name: "ussuri" - openstack_version: "stable/ussuri" - ubuntu_version: "18.04" - - name: "train" - openstack_version: "stable/train" - ubuntu_version: "18.04" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Deploy OpenStack ${{ matrix.name }} with Swift and run objectstorage acceptance tests steps: diff --git a/.github/workflows/functional-orchestration.yaml b/.github/workflows/functional-orchestration.yaml index aabaa490db..e8321da433 100644 --- a/.github/workflows/functional-orchestration.yaml +++ b/.github/workflows/functional-orchestration.yaml @@ -29,12 +29,6 @@ jobs: - name: "victoria" openstack_version: "stable/victoria" ubuntu_version: "20.04" - - name: "ussuri" - openstack_version: "stable/ussuri" - ubuntu_version: "18.04" - - name: "train" - openstack_version: "stable/train" - ubuntu_version: "18.04" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Deploy OpenStack ${{ matrix.name }} with Heat and run orchestration acceptance tests steps: diff --git a/.github/workflows/functional-placement.yaml b/.github/workflows/functional-placement.yaml index 90ec6db7e4..02d12b386e 100644 --- a/.github/workflows/functional-placement.yaml +++ b/.github/workflows/functional-placement.yaml @@ -29,12 +29,6 @@ jobs: - name: "victoria" openstack_version: "stable/victoria" ubuntu_version: "20.04" - - name: "ussuri" - openstack_version: "stable/ussuri" - ubuntu_version: "18.04" - - name: "train" - openstack_version: "stable/train" - ubuntu_version: "18.04" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Deploy OpenStack ${{ matrix.name }} with Placement and run placement acceptance tests steps: diff --git a/.github/workflows/functional-sharedfilesystems.yaml b/.github/workflows/functional-sharedfilesystems.yaml index 4a9101584e..62d2d85f1c 100644 --- a/.github/workflows/functional-sharedfilesystems.yaml +++ b/.github/workflows/functional-sharedfilesystems.yaml @@ -29,12 +29,6 @@ jobs: - name: "victoria" openstack_version: "stable/victoria" ubuntu_version: "20.04" - - name: "ussuri" - openstack_version: "stable/ussuri" - ubuntu_version: "18.04" - - name: "train" - openstack_version: "stable/train" - ubuntu_version: "18.04" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Deploy OpenStack ${{ matrix.name }} with Manila and run sharedfilesystems acceptance tests steps: From d01bacaa4db4e7e887b5367fc950fae0a0864e10 Mon Sep 17 00:00:00 2001 From: nikParasyr <nik.parasyr@protonmail.com> Date: Fri, 28 Apr 2023 15:33:13 +0200 Subject: [PATCH 233/360] Add PUT for indentity/v3/OS-INHERIT Add PUT for indentity/v3/OS-INHERIT. Follow the same pattern as normal role assignments for consistensy and minimalization. This adds PUT for all combinations of domain/project and user/group. --- .../openstack/identity/v3/osinherit_test.go | 171 ++++++++++++++++++ openstack/identity/v3/osinherit/doc.go | 35 ++++ openstack/identity/v3/osinherit/requests.go | 60 ++++++ openstack/identity/v3/osinherit/results.go | 9 + .../identity/v3/osinherit/testing/doc.go | 2 + .../identity/v3/osinherit/testing/fixtures.go | 35 ++++ .../v3/osinherit/testing/requests_test.go | 39 ++++ openstack/identity/v3/osinherit/urls.go | 11 ++ 8 files changed, 362 insertions(+) create mode 100644 acceptance/openstack/identity/v3/osinherit_test.go create mode 100644 openstack/identity/v3/osinherit/doc.go create mode 100644 openstack/identity/v3/osinherit/requests.go create mode 100644 openstack/identity/v3/osinherit/results.go create mode 100644 openstack/identity/v3/osinherit/testing/doc.go create mode 100644 openstack/identity/v3/osinherit/testing/fixtures.go create mode 100644 openstack/identity/v3/osinherit/testing/requests_test.go create mode 100644 openstack/identity/v3/osinherit/urls.go diff --git a/acceptance/openstack/identity/v3/osinherit_test.go b/acceptance/openstack/identity/v3/osinherit_test.go new file mode 100644 index 0000000000..8edd3bf919 --- /dev/null +++ b/acceptance/openstack/identity/v3/osinherit_test.go @@ -0,0 +1,171 @@ +//go:build acceptance +// +build acceptance + +package v3 + +import ( + "testing" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/openstack/identity/v3/domains" + "github.com/gophercloud/gophercloud/openstack/identity/v3/groups" + "github.com/gophercloud/gophercloud/openstack/identity/v3/osinherit" + "github.com/gophercloud/gophercloud/openstack/identity/v3/roles" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestInheritRolesAssignToUserOnProject(t *testing.T) { + clients.RequireAdmin(t) + + client, err := clients.NewIdentityV3Client() + th.AssertNoErr(t, err) + + project, err := CreateProject(t, client, nil) + th.AssertNoErr(t, err) + defer DeleteProject(t, client, project.ID) + + roleCreateOpts := roles.CreateOpts{ + DomainID: "default", + } + role, err := CreateRole(t, client, &roleCreateOpts) + th.AssertNoErr(t, err) + defer DeleteRole(t, client, role.ID) + + user, err := CreateUser(t, client, nil) + th.AssertNoErr(t, err) + defer DeleteUser(t, client, user.ID) + + t.Logf("Attempting to assign an inherited role %s to a user %s on a project %s", + role.Name, user.Name, project.Name) + + assignOpts := osinherit.AssignOpts{ + UserID: user.ID, + ProjectID: project.ID, + } + err = osinherit.Assign(client, role.ID, assignOpts).ExtractErr() + th.AssertNoErr(t, err) + + t.Logf("Successfully assigned a role %s to a user %s on a project %s", + role.Name, user.Name, project.Name) + +} + +func TestInheritRolesAssignToUserOnDomain(t *testing.T) { + clients.RequireAdmin(t) + + client, err := clients.NewIdentityV3Client() + th.AssertNoErr(t, err) + + domain, err := CreateDomain(t, client, &domains.CreateOpts{ + Enabled: gophercloud.Disabled, + }) + th.AssertNoErr(t, err) + defer DeleteDomain(t, client, domain.ID) + + roleCreateOpts := roles.CreateOpts{ + DomainID: "default", + } + role, err := CreateRole(t, client, &roleCreateOpts) + th.AssertNoErr(t, err) + defer DeleteRole(t, client, role.ID) + + user, err := CreateUser(t, client, nil) + th.AssertNoErr(t, err) + defer DeleteUser(t, client, user.ID) + + t.Logf("Attempting to assign a role %s to a user %s on a domain %s", + role.Name, user.Name, domain.Name) + + assignOpts := osinherit.AssignOpts{ + UserID: user.ID, + DomainID: domain.ID, + } + + err = osinherit.Assign(client, role.ID, assignOpts).ExtractErr() + th.AssertNoErr(t, err) + + t.Logf("Successfully assigned a role %s to a user %s on a domain %s", + role.Name, user.Name, domain.Name) + +} + +func TestInheritRolesAssignToGroupOnDomain(t *testing.T) { + clients.RequireAdmin(t) + + client, err := clients.NewIdentityV3Client() + th.AssertNoErr(t, err) + + domain, err := CreateDomain(t, client, &domains.CreateOpts{ + Enabled: gophercloud.Disabled, + }) + th.AssertNoErr(t, err) + defer DeleteDomain(t, client, domain.ID) + + roleCreateOpts := roles.CreateOpts{ + DomainID: "default", + } + role, err := CreateRole(t, client, &roleCreateOpts) + th.AssertNoErr(t, err) + defer DeleteRole(t, client, role.ID) + + groupCreateOpts := &groups.CreateOpts{ + DomainID: "default", + } + group, err := CreateGroup(t, client, groupCreateOpts) + th.AssertNoErr(t, err) + defer DeleteGroup(t, client, group.ID) + + t.Logf("Attempting to assign a role %s to a group %s on a domain %s", + role.Name, group.Name, domain.Name) + + assignOpts := osinherit.AssignOpts{ + GroupID: group.ID, + DomainID: domain.ID, + } + + err = osinherit.Assign(client, role.ID, assignOpts).ExtractErr() + th.AssertNoErr(t, err) + + t.Logf("Successfully assigned a role %s to a group %s on a domain %s", + role.Name, group.Name, domain.Name) +} + +func TestInheritRolesAssignToGroupOnProject(t *testing.T) { + clients.RequireAdmin(t) + + client, err := clients.NewIdentityV3Client() + th.AssertNoErr(t, err) + + project, err := CreateProject(t, client, nil) + th.AssertNoErr(t, err) + defer DeleteProject(t, client, project.ID) + + roleCreateOpts := roles.CreateOpts{ + DomainID: "default", + } + role, err := CreateRole(t, client, &roleCreateOpts) + th.AssertNoErr(t, err) + defer DeleteRole(t, client, role.ID) + + groupCreateOpts := &groups.CreateOpts{ + DomainID: "default", + } + group, err := CreateGroup(t, client, groupCreateOpts) + th.AssertNoErr(t, err) + defer DeleteGroup(t, client, group.ID) + + t.Logf("Attempting to assign a role %s to a group %s on a project %s", + role.Name, group.Name, project.Name) + + assignOpts := osinherit.AssignOpts{ + GroupID: group.ID, + ProjectID: project.ID, + } + err = osinherit.Assign(client, role.ID, assignOpts).ExtractErr() + th.AssertNoErr(t, err) + + t.Logf("Successfully assigned a role %s to a group %s on a project %s", + role.Name, group.Name, project.Name) + +} diff --git a/openstack/identity/v3/osinherit/doc.go b/openstack/identity/v3/osinherit/doc.go new file mode 100644 index 0000000000..1264b7b4bf --- /dev/null +++ b/openstack/identity/v3/osinherit/doc.go @@ -0,0 +1,35 @@ +/* +Package osinherit enables projects to inherit role assignments from +either their owning domain or projects that are higher in the hierarchy. + +Example to Assign a Inherited Role to a User to a Domain + + domainID := "a99e9b4e620e4db09a2dfb6e42a01e66" + userID := "9df1a02f5eb2416a9781e8b0c022d3ae" + roleID := "9fe2ff9ee4384b1894a90878d3e92bab" + + err := osinherit.Assign(identityClient, roleID, osinherit.AssignOpts{ + UserID: userID, + domainID: domainID, + }).ExtractErr() + + if err != nil { + panic(err) + } + +Example to Assign a Inherited Role to a User to a Project's subtree + + projectID := "a99e9b4e620e4db09a2dfb6e42a01e66" + userID := "9df1a02f5eb2416a9781e8b0c022d3ae" + roleID := "9fe2ff9ee4384b1894a90878d3e92bab" + + err := osinherit.Assign(identityClient, roleID, osinherit.AssignOpts{ + UserID: userID, + ProjectID: projectID, + }).ExtractErr() + + if err != nil { + panic(err) + } +*/ +package osinherit diff --git a/openstack/identity/v3/osinherit/requests.go b/openstack/identity/v3/osinherit/requests.go new file mode 100644 index 0000000000..d0da2232dd --- /dev/null +++ b/openstack/identity/v3/osinherit/requests.go @@ -0,0 +1,60 @@ +package osinherit + +import "github.com/gophercloud/gophercloud" + +// AssignOpts provides options to assign a role +type AssignOpts struct { + // UserID is the ID of a user to assign a inherited role + // Note: exactly one of UserID or GroupID must be provided + UserID string `xor:"GroupID"` + + // GroupID is the ID of a group to assign a inherited role + // Note: exactly one of UserID or GroupID must be provided + GroupID string `xor:"UserID"` + + // ProjectID is the ID of a project to assign a inherited role on + // Note: exactly one of ProjectID or DomainID must be provided + ProjectID string `xor:"DomainID"` + + // DomainID is the ID of a domain to assign a inherited role on + // Note: exactly one of ProjectID or DomainID must be provided + DomainID string `xor:"ProjectID"` +} + +// Assign is the operation responsible for assigning a inherited role +// to a user/group on a project/domain. +func Assign(client *gophercloud.ServiceClient, roleID string, opts AssignOpts) (r AssignmentResult) { + // Check xor conditions + _, err := gophercloud.BuildRequestBody(opts, "") + if err != nil { + r.Err = err + return + } + + // Get corresponding URL + var targetID string + var targetType string + if opts.ProjectID != "" { + targetID = opts.ProjectID + targetType = "projects" + } else { + targetID = opts.DomainID + targetType = "domains" + } + + var actorID string + var actorType string + if opts.UserID != "" { + actorID = opts.UserID + actorType = "users" + } else { + actorID = opts.GroupID + actorType = "groups" + } + + resp, err := client.Put(assignURL(client, targetType, targetID, actorType, actorID, roleID), nil, nil, &gophercloud.RequestOpts{ + OkCodes: []int{204}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} diff --git a/openstack/identity/v3/osinherit/results.go b/openstack/identity/v3/osinherit/results.go new file mode 100644 index 0000000000..5f1ec7e07d --- /dev/null +++ b/openstack/identity/v3/osinherit/results.go @@ -0,0 +1,9 @@ +package osinherit + +import "github.com/gophercloud/gophercloud" + +// AssignmentResult represents the result of an assign operation. +// Call ExtractErr method to determine if the request succeeded or failed. +type AssignmentResult struct { + gophercloud.ErrResult +} diff --git a/openstack/identity/v3/osinherit/testing/doc.go b/openstack/identity/v3/osinherit/testing/doc.go new file mode 100644 index 0000000000..a71ed9d2ea --- /dev/null +++ b/openstack/identity/v3/osinherit/testing/doc.go @@ -0,0 +1,2 @@ +// osinherit unit tests +package testing diff --git a/openstack/identity/v3/osinherit/testing/fixtures.go b/openstack/identity/v3/osinherit/testing/fixtures.go new file mode 100644 index 0000000000..a9bc3725e8 --- /dev/null +++ b/openstack/identity/v3/osinherit/testing/fixtures.go @@ -0,0 +1,35 @@ +package testing + +import ( + "net/http" + "testing" + + th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/testhelper/client" +) + +func HandleAssignSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/OS-INHERIT/projects/{project_id}/users/{user_id}/roles/{role_id}/inherited_to_projects", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PUT") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + w.WriteHeader(http.StatusNoContent) + }) + + th.Mux.HandleFunc("/OS-INHERIT/projects/{project_id}/groups/{group_id}/roles/{role_id}/inherited_to_projects", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PUT") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + w.WriteHeader(http.StatusNoContent) + }) + + th.Mux.HandleFunc("/OS-INHERIT/domains/{domain_id}/users/{user_id}/roles/{role_id}/inherited_to_projects", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PUT") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + w.WriteHeader(http.StatusNoContent) + }) + + th.Mux.HandleFunc("/OS-INHERIT/domains/{domain_id}/groups/{group_id}/roles/{role_id}/inherited_to_projects", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PUT") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + w.WriteHeader(http.StatusNoContent) + }) +} diff --git a/openstack/identity/v3/osinherit/testing/requests_test.go b/openstack/identity/v3/osinherit/testing/requests_test.go new file mode 100644 index 0000000000..c1e540c7f6 --- /dev/null +++ b/openstack/identity/v3/osinherit/testing/requests_test.go @@ -0,0 +1,39 @@ +package testing + +import ( + "testing" + + "github.com/gophercloud/gophercloud/openstack/identity/v3/osinherit" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestAssign(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleAssignSuccessfully(t) + + err := osinherit.Assign(client.ServiceClient(), "{role_id}", osinherit.AssignOpts{ + UserID: "{user_id}", + ProjectID: "{project_id}", + }).ExtractErr() + th.AssertNoErr(t, err) + + err = osinherit.Assign(client.ServiceClient(), "{role_id}", osinherit.AssignOpts{ + UserID: "{user_id}", + DomainID: "{domain_id}", + }).ExtractErr() + th.AssertNoErr(t, err) + + err = osinherit.Assign(client.ServiceClient(), "{role_id}", osinherit.AssignOpts{ + GroupID: "{group_id}", + ProjectID: "{project_id}", + }).ExtractErr() + th.AssertNoErr(t, err) + + err = osinherit.Assign(client.ServiceClient(), "{role_id}", osinherit.AssignOpts{ + GroupID: "{group_id}", + DomainID: "{domain_id}", + }).ExtractErr() + th.AssertNoErr(t, err) +} diff --git a/openstack/identity/v3/osinherit/urls.go b/openstack/identity/v3/osinherit/urls.go new file mode 100644 index 0000000000..358dc33ac2 --- /dev/null +++ b/openstack/identity/v3/osinherit/urls.go @@ -0,0 +1,11 @@ +package osinherit + +import "github.com/gophercloud/gophercloud" + +const ( + inheritPath = "OS-INHERIT" +) + +func assignURL(client *gophercloud.ServiceClient, targetType, targetID, actorType, actorID, roleID string) string { + return client.ServiceURL(inheritPath, targetType, targetID, actorType, actorID, "roles", roleID, "inherited_to_projects") +} From e149528997e4d7d7e7e1e63cb7f8844269ecc609 Mon Sep 17 00:00:00 2001 From: nikParasyr <nik.parasyr@protonmail.com> Date: Fri, 28 Apr 2023 16:09:59 +0200 Subject: [PATCH 234/360] Add HEAD to identity/v3/OS-INHERIT Add HEAD to identity/v3/OS-INHERIT. These calls can be used to validate the existance of various inherited roles. Follow similar conventions as identity/v3/roles for consistency --- .../openstack/identity/v3/osinherit_test.go | 43 ++++++++++++++ openstack/identity/v3/osinherit/doc.go | 15 +++++ openstack/identity/v3/osinherit/requests.go | 57 +++++++++++++++++++ openstack/identity/v3/osinherit/results.go | 6 ++ .../identity/v3/osinherit/testing/fixtures.go | 26 +++++++++ .../v3/osinherit/testing/requests_test.go | 30 ++++++++++ 6 files changed, 177 insertions(+) diff --git a/acceptance/openstack/identity/v3/osinherit_test.go b/acceptance/openstack/identity/v3/osinherit_test.go index 8edd3bf919..b837307fb5 100644 --- a/acceptance/openstack/identity/v3/osinherit_test.go +++ b/acceptance/openstack/identity/v3/osinherit_test.go @@ -49,6 +49,16 @@ func TestInheritRolesAssignToUserOnProject(t *testing.T) { t.Logf("Successfully assigned a role %s to a user %s on a project %s", role.Name, user.Name, project.Name) + validateOpts := osinherit.ValidateOpts{ + UserID: user.ID, + ProjectID: project.ID, + } + err = osinherit.Validate(client, role.ID, validateOpts).ExtractErr() + th.AssertNoErr(t, err) + + t.Logf("Successfully validated inherited role %s to a user %s on a project %s", + role.Name, user.Name, project.Name) + } func TestInheritRolesAssignToUserOnDomain(t *testing.T) { @@ -88,6 +98,17 @@ func TestInheritRolesAssignToUserOnDomain(t *testing.T) { t.Logf("Successfully assigned a role %s to a user %s on a domain %s", role.Name, user.Name, domain.Name) + validateOpts := osinherit.ValidateOpts{ + UserID: user.ID, + DomainID: domain.ID, + } + + err = osinherit.Validate(client, role.ID, validateOpts).ExtractErr() + th.AssertNoErr(t, err) + + t.Logf("Successfully validated inherited role %s to a user %s on a domain %s", + role.Name, user.Name, domain.Name) + } func TestInheritRolesAssignToGroupOnDomain(t *testing.T) { @@ -129,6 +150,18 @@ func TestInheritRolesAssignToGroupOnDomain(t *testing.T) { t.Logf("Successfully assigned a role %s to a group %s on a domain %s", role.Name, group.Name, domain.Name) + + validateOpts := osinherit.ValidateOpts{ + GroupID: group.ID, + DomainID: domain.ID, + } + + err = osinherit.Validate(client, role.ID, validateOpts).ExtractErr() + th.AssertNoErr(t, err) + + t.Logf("Successfully validated inherited role %s to a group %s on a domain %s", + role.Name, group.Name, domain.Name) + } func TestInheritRolesAssignToGroupOnProject(t *testing.T) { @@ -168,4 +201,14 @@ func TestInheritRolesAssignToGroupOnProject(t *testing.T) { t.Logf("Successfully assigned a role %s to a group %s on a project %s", role.Name, group.Name, project.Name) + validateOpts := osinherit.ValidateOpts{ + GroupID: group.ID, + ProjectID: project.ID, + } + err = osinherit.Validate(client, role.ID, validateOpts).ExtractErr() + th.AssertNoErr(t, err) + + t.Logf("Successfully validated inherited role %s to a group %s on a project %s", + role.Name, group.Name, project.Name) + } diff --git a/openstack/identity/v3/osinherit/doc.go b/openstack/identity/v3/osinherit/doc.go index 1264b7b4bf..7fb9fac092 100644 --- a/openstack/identity/v3/osinherit/doc.go +++ b/openstack/identity/v3/osinherit/doc.go @@ -28,6 +28,21 @@ Example to Assign a Inherited Role to a User to a Project's subtree ProjectID: projectID, }).ExtractErr() + if err != nil { + panic(err) + } + +Example to validate a Inherited Role to a User to a Project's subtree + + projectID := "a99e9b4e620e4db09a2dfb6e42a01e66" + userID := "9df1a02f5eb2416a9781e8b0c022d3ae" + roleID := "9fe2ff9ee4384b1894a90878d3e92bab" + + err := osinherit.Assign(identityClient, roleID, osinherit.AssignOpts{ + UserID: userID, + ProjectID: projectID, + }).ExtractErr() + if err != nil { panic(err) } diff --git a/openstack/identity/v3/osinherit/requests.go b/openstack/identity/v3/osinherit/requests.go index d0da2232dd..0d62810a46 100644 --- a/openstack/identity/v3/osinherit/requests.go +++ b/openstack/identity/v3/osinherit/requests.go @@ -21,6 +21,25 @@ type AssignOpts struct { DomainID string `xor:"ProjectID"` } +// ValidateOpts provides options to which role to validate +type ValidateOpts struct { + // UserID is the ID of a user to validate an inherited role + // Note: exactly one of UserID or GroupID must be provided + UserID string `xor:"GroupID"` + + // GroupID is the ID of a group to validate an inherited role + // Note: exactly one of UserID or GroupID must be provided + GroupID string `xor:"UserID"` + + // ProjectID is the ID of a project to validate an inherited role on + // Note: exactly one of ProjectID or DomainID must be provided + ProjectID string `xor:"DomainID"` + + // DomainID is the ID of a domain to validate an inherited role on + // Note: exactly one of ProjectID or DomainID must be provided + DomainID string `xor:"ProjectID"` +} + // Assign is the operation responsible for assigning a inherited role // to a user/group on a project/domain. func Assign(client *gophercloud.ServiceClient, roleID string, opts AssignOpts) (r AssignmentResult) { @@ -58,3 +77,41 @@ func Assign(client *gophercloud.ServiceClient, roleID string, opts AssignOpts) ( _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } + +// Validate is the operation responsible for validating an inherited role +// of a user/group on a project/domain. +func Validate(client *gophercloud.ServiceClient, roleID string, opts ValidateOpts) (r ValidateResult) { + // Check xor conditions + _, err := gophercloud.BuildRequestBody(opts, "") + if err != nil { + r.Err = err + return + } + + // Get corresponding URL + var targetID string + var targetType string + if opts.ProjectID != "" { + targetID = opts.ProjectID + targetType = "projects" + } else { + targetID = opts.DomainID + targetType = "domains" + } + + var actorID string + var actorType string + if opts.UserID != "" { + actorID = opts.UserID + actorType = "users" + } else { + actorID = opts.GroupID + actorType = "groups" + } + + resp, err := client.Head(assignURL(client, targetType, targetID, actorType, actorID, roleID), &gophercloud.RequestOpts{ + OkCodes: []int{204}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} diff --git a/openstack/identity/v3/osinherit/results.go b/openstack/identity/v3/osinherit/results.go index 5f1ec7e07d..ff781a2e71 100644 --- a/openstack/identity/v3/osinherit/results.go +++ b/openstack/identity/v3/osinherit/results.go @@ -7,3 +7,9 @@ import "github.com/gophercloud/gophercloud" type AssignmentResult struct { gophercloud.ErrResult } + +// ValidateResult represents the result of an validate operation. +// Call ExtractErr method to determine if the request succeeded or failed. +type ValidateResult struct { + gophercloud.ErrResult +} diff --git a/openstack/identity/v3/osinherit/testing/fixtures.go b/openstack/identity/v3/osinherit/testing/fixtures.go index a9bc3725e8..1001fd9890 100644 --- a/openstack/identity/v3/osinherit/testing/fixtures.go +++ b/openstack/identity/v3/osinherit/testing/fixtures.go @@ -33,3 +33,29 @@ func HandleAssignSuccessfully(t *testing.T) { w.WriteHeader(http.StatusNoContent) }) } + +func HandleValidateSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/OS-INHERIT/projects/{project_id}/users/{user_id}/roles/{role_id}/inherited_to_projects", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "HEAD") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + w.WriteHeader(http.StatusNoContent) + }) + + th.Mux.HandleFunc("/OS-INHERIT/projects/{project_id}/groups/{group_id}/roles/{role_id}/inherited_to_projects", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "HEAD") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + w.WriteHeader(http.StatusNoContent) + }) + + th.Mux.HandleFunc("/OS-INHERIT/domains/{domain_id}/users/{user_id}/roles/{role_id}/inherited_to_projects", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "HEAD") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + w.WriteHeader(http.StatusNoContent) + }) + + th.Mux.HandleFunc("/OS-INHERIT/domains/{domain_id}/groups/{group_id}/roles/{role_id}/inherited_to_projects", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "HEAD") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + w.WriteHeader(http.StatusNoContent) + }) +} diff --git a/openstack/identity/v3/osinherit/testing/requests_test.go b/openstack/identity/v3/osinherit/testing/requests_test.go index c1e540c7f6..392c9d49d1 100644 --- a/openstack/identity/v3/osinherit/testing/requests_test.go +++ b/openstack/identity/v3/osinherit/testing/requests_test.go @@ -37,3 +37,33 @@ func TestAssign(t *testing.T) { }).ExtractErr() th.AssertNoErr(t, err) } + +func TestValidate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleValidateSuccessfully(t) + + err := osinherit.Validate(client.ServiceClient(), "{role_id}", osinherit.ValidateOpts{ + UserID: "{user_id}", + ProjectID: "{project_id}", + }).ExtractErr() + th.AssertNoErr(t, err) + + err = osinherit.Validate(client.ServiceClient(), "{role_id}", osinherit.ValidateOpts{ + UserID: "{user_id}", + DomainID: "{domain_id}", + }).ExtractErr() + th.AssertNoErr(t, err) + + err = osinherit.Validate(client.ServiceClient(), "{role_id}", osinherit.ValidateOpts{ + GroupID: "{group_id}", + ProjectID: "{project_id}", + }).ExtractErr() + th.AssertNoErr(t, err) + + err = osinherit.Validate(client.ServiceClient(), "{role_id}", osinherit.ValidateOpts{ + GroupID: "{group_id}", + DomainID: "{domain_id}", + }).ExtractErr() + th.AssertNoErr(t, err) +} From e34f968bf3bf3ebdc06cc7176211d2b91017999e Mon Sep 17 00:00:00 2001 From: nikParasyr <nik.parasyr@protonmail.com> Date: Fri, 28 Apr 2023 16:37:50 +0200 Subject: [PATCH 235/360] Add DELETE for identity/v3/OS-INHERIT Add DELETE for identity/v3/OS-INHERIT. These calls can be used to unassign various inherited roles. Follow similar conventions as identity/v3/roles for consistency --- .../openstack/identity/v3/osinherit_test.go | 42 +++++++++++ openstack/identity/v3/osinherit/doc.go | 17 ++++- openstack/identity/v3/osinherit/requests.go | 69 +++++++++++++++++-- openstack/identity/v3/osinherit/results.go | 6 ++ .../identity/v3/osinherit/testing/fixtures.go | 26 +++++++ .../v3/osinherit/testing/requests_test.go | 30 ++++++++ 6 files changed, 183 insertions(+), 7 deletions(-) diff --git a/acceptance/openstack/identity/v3/osinherit_test.go b/acceptance/openstack/identity/v3/osinherit_test.go index b837307fb5..0ac1eb7846 100644 --- a/acceptance/openstack/identity/v3/osinherit_test.go +++ b/acceptance/openstack/identity/v3/osinherit_test.go @@ -59,6 +59,16 @@ func TestInheritRolesAssignToUserOnProject(t *testing.T) { t.Logf("Successfully validated inherited role %s to a user %s on a project %s", role.Name, user.Name, project.Name) + unassignOpts := osinherit.UnassignOpts{ + UserID: user.ID, + ProjectID: project.ID, + } + err = osinherit.Unassign(client, role.ID, unassignOpts).ExtractErr() + th.AssertNoErr(t, err) + + t.Logf("Successfully unassgined inherited role %s to a user %s on a project %s", + role.Name, user.Name, project.Name) + } func TestInheritRolesAssignToUserOnDomain(t *testing.T) { @@ -109,6 +119,17 @@ func TestInheritRolesAssignToUserOnDomain(t *testing.T) { t.Logf("Successfully validated inherited role %s to a user %s on a domain %s", role.Name, user.Name, domain.Name) + unassignOpts := osinherit.UnassignOpts{ + UserID: user.ID, + DomainID: domain.ID, + } + + err = osinherit.Unassign(client, role.ID, unassignOpts).ExtractErr() + th.AssertNoErr(t, err) + + t.Logf("Successfully unassigned inherited role %s to a user %s on a domain %s", + role.Name, user.Name, domain.Name) + } func TestInheritRolesAssignToGroupOnDomain(t *testing.T) { @@ -162,6 +183,17 @@ func TestInheritRolesAssignToGroupOnDomain(t *testing.T) { t.Logf("Successfully validated inherited role %s to a group %s on a domain %s", role.Name, group.Name, domain.Name) + unassignOpts := osinherit.UnassignOpts{ + GroupID: group.ID, + DomainID: domain.ID, + } + + err = osinherit.Unassign(client, role.ID, unassignOpts).ExtractErr() + th.AssertNoErr(t, err) + + t.Logf("Successfully unassigned inherited role %s to a group %s on a domain %s", + role.Name, group.Name, domain.Name) + } func TestInheritRolesAssignToGroupOnProject(t *testing.T) { @@ -211,4 +243,14 @@ func TestInheritRolesAssignToGroupOnProject(t *testing.T) { t.Logf("Successfully validated inherited role %s to a group %s on a project %s", role.Name, group.Name, project.Name) + unassignOpts := osinherit.UnassignOpts{ + GroupID: group.ID, + ProjectID: project.ID, + } + err = osinherit.Unassign(client, role.ID, unassignOpts).ExtractErr() + th.AssertNoErr(t, err) + + t.Logf("Successfully unassgined inherited role %s to a group %s on a project %s", + role.Name, group.Name, project.Name) + } diff --git a/openstack/identity/v3/osinherit/doc.go b/openstack/identity/v3/osinherit/doc.go index 7fb9fac092..e979f7710c 100644 --- a/openstack/identity/v3/osinherit/doc.go +++ b/openstack/identity/v3/osinherit/doc.go @@ -38,7 +38,22 @@ Example to validate a Inherited Role to a User to a Project's subtree userID := "9df1a02f5eb2416a9781e8b0c022d3ae" roleID := "9fe2ff9ee4384b1894a90878d3e92bab" - err := osinherit.Assign(identityClient, roleID, osinherit.AssignOpts{ + err := osinherit.Validate(identityClient, roleID, osinherit.validateOpts{ + UserID: userID, + ProjectID: projectID, + }).ExtractErr() + + if err != nil { + panic(err) + } + +Example to unassign a Inherited Role to a User to a Project's subtree + + projectID := "a99e9b4e620e4db09a2dfb6e42a01e66" + userID := "9df1a02f5eb2416a9781e8b0c022d3ae" + roleID := "9fe2ff9ee4384b1894a90878d3e92bab" + + err := osinherit.Unassign(identityClient, roleID, osinherit.UnassignOpts{ UserID: userID, ProjectID: projectID, }).ExtractErr() diff --git a/openstack/identity/v3/osinherit/requests.go b/openstack/identity/v3/osinherit/requests.go index 0d62810a46..fe261660d8 100644 --- a/openstack/identity/v3/osinherit/requests.go +++ b/openstack/identity/v3/osinherit/requests.go @@ -2,21 +2,21 @@ package osinherit import "github.com/gophercloud/gophercloud" -// AssignOpts provides options to assign a role +// AssignOpts provides options to assign an inherited role type AssignOpts struct { - // UserID is the ID of a user to assign a inherited role + // UserID is the ID of a user to assign an inherited role // Note: exactly one of UserID or GroupID must be provided UserID string `xor:"GroupID"` - // GroupID is the ID of a group to assign a inherited role + // GroupID is the ID of a group to assign an inherited role // Note: exactly one of UserID or GroupID must be provided GroupID string `xor:"UserID"` - // ProjectID is the ID of a project to assign a inherited role on + // ProjectID is the ID of a project to assign an inherited role on // Note: exactly one of ProjectID or DomainID must be provided ProjectID string `xor:"DomainID"` - // DomainID is the ID of a domain to assign a inherited role on + // DomainID is the ID of a domain to assign an inherited role on // Note: exactly one of ProjectID or DomainID must be provided DomainID string `xor:"ProjectID"` } @@ -40,7 +40,26 @@ type ValidateOpts struct { DomainID string `xor:"ProjectID"` } -// Assign is the operation responsible for assigning a inherited role +// UnassignOpts provides options to unassign an inherited role +type UnassignOpts struct { + // UserID is the ID of a user to unassign an inherited role + // Note: exactly one of UserID or GroupID must be provided + UserID string `xor:"GroupID"` + + // GroupID is the ID of a group to unassign an inherited role + // Note: exactly one of UserID or GroupID must be provided + GroupID string `xor:"UserID"` + + // ProjectID is the ID of a project to assign an inherited role on + // Note: exactly one of ProjectID or DomainID must be provided + ProjectID string `xor:"DomainID"` + + // DomainID is the ID of a domain to assign an inherited role on + // Note: exactly one of ProjectID or DomainID must be provided + DomainID string `xor:"ProjectID"` +} + +// Assign is the operation responsible for assigning an inherited role // to a user/group on a project/domain. func Assign(client *gophercloud.ServiceClient, roleID string, opts AssignOpts) (r AssignmentResult) { // Check xor conditions @@ -115,3 +134,41 @@ func Validate(client *gophercloud.ServiceClient, roleID string, opts ValidateOpt _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } + +// Unassign is the operation responsible for unassigning an inherited +// role to a user/group on a project/domain. +func Unassign(client *gophercloud.ServiceClient, roleID string, opts UnassignOpts) (r UnassignmentResult) { + // Check xor conditions + _, err := gophercloud.BuildRequestBody(opts, "") + if err != nil { + r.Err = err + return + } + + // Get corresponding URL + var targetID string + var targetType string + if opts.ProjectID != "" { + targetID = opts.ProjectID + targetType = "projects" + } else { + targetID = opts.DomainID + targetType = "domains" + } + + var actorID string + var actorType string + if opts.UserID != "" { + actorID = opts.UserID + actorType = "users" + } else { + actorID = opts.GroupID + actorType = "groups" + } + + resp, err := client.Delete(assignURL(client, targetType, targetID, actorType, actorID, roleID), &gophercloud.RequestOpts{ + OkCodes: []int{204}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} diff --git a/openstack/identity/v3/osinherit/results.go b/openstack/identity/v3/osinherit/results.go index ff781a2e71..ffbc83dfea 100644 --- a/openstack/identity/v3/osinherit/results.go +++ b/openstack/identity/v3/osinherit/results.go @@ -13,3 +13,9 @@ type AssignmentResult struct { type ValidateResult struct { gophercloud.ErrResult } + +// UnassignmentResult represents the result of an unassign operation. +// Call ExtractErr method to determine if the request succeeded or failed. +type UnassignmentResult struct { + gophercloud.ErrResult +} diff --git a/openstack/identity/v3/osinherit/testing/fixtures.go b/openstack/identity/v3/osinherit/testing/fixtures.go index 1001fd9890..49180035bc 100644 --- a/openstack/identity/v3/osinherit/testing/fixtures.go +++ b/openstack/identity/v3/osinherit/testing/fixtures.go @@ -59,3 +59,29 @@ func HandleValidateSuccessfully(t *testing.T) { w.WriteHeader(http.StatusNoContent) }) } + +func HandleUnassignSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/OS-INHERIT/projects/{project_id}/users/{user_id}/roles/{role_id}/inherited_to_projects", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "DELETE") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + w.WriteHeader(http.StatusNoContent) + }) + + th.Mux.HandleFunc("/OS-INHERIT/projects/{project_id}/groups/{group_id}/roles/{role_id}/inherited_to_projects", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "DELETE") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + w.WriteHeader(http.StatusNoContent) + }) + + th.Mux.HandleFunc("/OS-INHERIT/domains/{domain_id}/users/{user_id}/roles/{role_id}/inherited_to_projects", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "DELETE") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + w.WriteHeader(http.StatusNoContent) + }) + + th.Mux.HandleFunc("/OS-INHERIT/domains/{domain_id}/groups/{group_id}/roles/{role_id}/inherited_to_projects", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "DELETE") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + w.WriteHeader(http.StatusNoContent) + }) +} diff --git a/openstack/identity/v3/osinherit/testing/requests_test.go b/openstack/identity/v3/osinherit/testing/requests_test.go index 392c9d49d1..e85703aa38 100644 --- a/openstack/identity/v3/osinherit/testing/requests_test.go +++ b/openstack/identity/v3/osinherit/testing/requests_test.go @@ -67,3 +67,33 @@ func TestValidate(t *testing.T) { }).ExtractErr() th.AssertNoErr(t, err) } + +func TestUnassign(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleUnassignSuccessfully(t) + + err := osinherit.Unassign(client.ServiceClient(), "{role_id}", osinherit.UnassignOpts{ + UserID: "{user_id}", + ProjectID: "{project_id}", + }).ExtractErr() + th.AssertNoErr(t, err) + + err = osinherit.Unassign(client.ServiceClient(), "{role_id}", osinherit.UnassignOpts{ + UserID: "{user_id}", + DomainID: "{domain_id}", + }).ExtractErr() + th.AssertNoErr(t, err) + + err = osinherit.Unassign(client.ServiceClient(), "{role_id}", osinherit.UnassignOpts{ + GroupID: "{group_id}", + ProjectID: "{project_id}", + }).ExtractErr() + th.AssertNoErr(t, err) + + err = osinherit.Unassign(client.ServiceClient(), "{role_id}", osinherit.UnassignOpts{ + GroupID: "{group_id}", + DomainID: "{domain_id}", + }).ExtractErr() + th.AssertNoErr(t, err) +} From 7e94efb477a8e09c97da48d2217633de171f3d61 Mon Sep 17 00:00:00 2001 From: nikParasyr <nik.parasyr@protonmail.com> Date: Sun, 14 May 2023 15:31:50 +0200 Subject: [PATCH 236/360] Fix typos and add validation tests --- .../openstack/identity/v3/osinherit_test.go | 4 +-- .../v3/osinherit/testing/requests_test.go | 36 +++++++++++++++++++ 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/acceptance/openstack/identity/v3/osinherit_test.go b/acceptance/openstack/identity/v3/osinherit_test.go index 0ac1eb7846..6ac777c0ec 100644 --- a/acceptance/openstack/identity/v3/osinherit_test.go +++ b/acceptance/openstack/identity/v3/osinherit_test.go @@ -66,7 +66,7 @@ func TestInheritRolesAssignToUserOnProject(t *testing.T) { err = osinherit.Unassign(client, role.ID, unassignOpts).ExtractErr() th.AssertNoErr(t, err) - t.Logf("Successfully unassgined inherited role %s to a user %s on a project %s", + t.Logf("Successfully unassigned inherited role %s to a user %s on a project %s", role.Name, user.Name, project.Name) } @@ -250,7 +250,7 @@ func TestInheritRolesAssignToGroupOnProject(t *testing.T) { err = osinherit.Unassign(client, role.ID, unassignOpts).ExtractErr() th.AssertNoErr(t, err) - t.Logf("Successfully unassgined inherited role %s to a group %s on a project %s", + t.Logf("Successfully unassigned inherited role %s to a group %s on a project %s", role.Name, group.Name, project.Name) } diff --git a/openstack/identity/v3/osinherit/testing/requests_test.go b/openstack/identity/v3/osinherit/testing/requests_test.go index e85703aa38..0c542ba41b 100644 --- a/openstack/identity/v3/osinherit/testing/requests_test.go +++ b/openstack/identity/v3/osinherit/testing/requests_test.go @@ -36,6 +36,18 @@ func TestAssign(t *testing.T) { DomainID: "{domain_id}", }).ExtractErr() th.AssertNoErr(t, err) + + err = osinherit.Assign(client.ServiceClient(), "{role_id}", osinherit.AssignOpts{ + GroupID: "{group_id}", + UserID: "{user_id}", + }).ExtractErr() + th.AssertErr(t, err) + + err = osinherit.Assign(client.ServiceClient(), "{role_id}", osinherit.AssignOpts{ + ProjectID: "{project_id}", + DomainID: "{domain_id}", + }).ExtractErr() + th.AssertErr(t, err) } func TestValidate(t *testing.T) { @@ -66,6 +78,18 @@ func TestValidate(t *testing.T) { DomainID: "{domain_id}", }).ExtractErr() th.AssertNoErr(t, err) + + err = osinherit.Validate(client.ServiceClient(), "{role_id}", osinherit.ValidateOpts{ + GroupID: "{group_id}", + UserID: "{user_id}", + }).ExtractErr() + th.AssertErr(t, err) + + err = osinherit.Validate(client.ServiceClient(), "{role_id}", osinherit.ValidateOpts{ + ProjectID: "{project_id}", + DomainID: "{domain_id}", + }).ExtractErr() + th.AssertErr(t, err) } func TestUnassign(t *testing.T) { @@ -96,4 +120,16 @@ func TestUnassign(t *testing.T) { DomainID: "{domain_id}", }).ExtractErr() th.AssertNoErr(t, err) + + err = osinherit.Unassign(client.ServiceClient(), "{role_id}", osinherit.UnassignOpts{ + GroupID: "{group_id}", + UserID: "{user_id}", + }).ExtractErr() + th.AssertErr(t, err) + + err = osinherit.Unassign(client.ServiceClient(), "{role_id}", osinherit.UnassignOpts{ + ProjectID: "{project_id}", + DomainID: "{domain_id}", + }).ExtractErr() + th.AssertErr(t, err) } From 4f9320f5b8686b4c9d0521da606778c7e61a086d Mon Sep 17 00:00:00 2001 From: Emil Maruszczak <emil.maruszczak@thehutgroup.com> Date: Thu, 29 Dec 2022 17:09:47 +0100 Subject: [PATCH 237/360] Add Update operation --- .../openstack/identity/v3/limits_test.go | 12 +++++ openstack/identity/v3/limits/doc.go | 16 ++++++ openstack/identity/v3/limits/requests.go | 34 ++++++++++++ openstack/identity/v3/limits/results.go | 6 +++ .../identity/v3/limits/testing/fixtures.go | 53 +++++++++++++++++++ .../v3/limits/testing/requests_test.go | 17 ++++++ 6 files changed, 138 insertions(+) diff --git a/acceptance/openstack/identity/v3/limits_test.go b/acceptance/openstack/identity/v3/limits_test.go index 9796a6de87..2de8233b7c 100644 --- a/acceptance/openstack/identity/v3/limits_test.go +++ b/acceptance/openstack/identity/v3/limits_test.go @@ -102,4 +102,16 @@ func TestLimitsCRUD(t *testing.T) { limit, err := limits.Get(client, limitID).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, createdLimits[0], *limit) + + newLimitDescription := tools.RandomString("TESTLIMITS-DESC-CHNGD-", 8) + newResourceLimit := tools.RandomInt(1, 100) + updateOpts := limits.UpdateOpts{ + Description: &newLimitDescription, + ResourceLimit: &newResourceLimit, + } + + updatedLimit, err := limits.Update(client, limitID, updateOpts).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, newLimitDescription, updatedLimit.Description) + th.AssertEquals(t, newResourceLimit, updatedLimit.ResourceLimit) } diff --git a/openstack/identity/v3/limits/doc.go b/openstack/identity/v3/limits/doc.go index 57a257717a..2c307636a5 100644 --- a/openstack/identity/v3/limits/doc.go +++ b/openstack/identity/v3/limits/doc.go @@ -55,5 +55,21 @@ Example to Get a Limit if err != nil { panic(err) } + +Example to Update a Limit + + limitID := "0fe36e73809d46aeae6705c39077b1b3" + + description := "Number of snapshots for project 3a705b9f56bb439381b43c4fe59dccce" + resourceLimit := 5 + updateOpts := limits.UpdateOpts{ + Description: &description, + ResourceLimit: &resourceLimit, + } + + limit, err := limits.Update(identityClient, limitID, updateOpts).Extract() + if err != nil { + panic(err) + } */ package limits diff --git a/openstack/identity/v3/limits/requests.go b/openstack/identity/v3/limits/requests.go index 38b0975c56..dc6bdba187 100644 --- a/openstack/identity/v3/limits/requests.go +++ b/openstack/identity/v3/limits/requests.go @@ -126,3 +126,37 @@ func Get(client *gophercloud.ServiceClient, limitID string) (r GetResult) { _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } + +// UpdateOptsBuilder allows extensions to add additional parameters to +// the Update request. +type UpdateOptsBuilder interface { + ToLimitUpdateMap() (map[string]interface{}, error) +} + +// UpdateOpts represents parameters to update a domain. +type UpdateOpts struct { + // Description of the limit. + Description *string `json:"description,omitempty"` + + // ResourceLimit is the override limit. + ResourceLimit *int `json:"resource_limit,omitempty"` +} + +// ToLimitUpdateMap formats UpdateOpts into an update request. +func (opts UpdateOpts) ToLimitUpdateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "limit") +} + +// Update modifies the attributes of a limit. +func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { + b, err := opts.ToLimitUpdateMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Patch(resourceURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} diff --git a/openstack/identity/v3/limits/results.go b/openstack/identity/v3/limits/results.go index 2f25edcb50..f6a1771400 100644 --- a/openstack/identity/v3/limits/results.go +++ b/openstack/identity/v3/limits/results.go @@ -90,6 +90,12 @@ type GetResult struct { commonResult } +// UpdateResult is the result of an Update request. Call its Extract method to +// interpret it as a Limit. +type UpdateResult struct { + commonResult +} + // IsEmpty determines whether or not a page of Limits contains any results. func (r LimitPage) IsEmpty() (bool, error) { if r.StatusCode == 204 { diff --git a/openstack/identity/v3/limits/testing/fixtures.go b/openstack/identity/v3/limits/testing/fixtures.go index d3a7330952..ecdeb2ca0d 100644 --- a/openstack/identity/v3/limits/testing/fixtures.go +++ b/openstack/identity/v3/limits/testing/fixtures.go @@ -96,6 +96,32 @@ const GetOutput = ` } ` +const UpdateRequest = ` +{ + "limit": { + "resource_limit": 5, + "description": "Number of snapshots for project 3a705b9f56bb439381b43c4fe59dccce" + } +} +` + +const UpdateOutput = ` +{ + "limit": { + "resource_name": "snapshot", + "region_id": "RegionOne", + "links": { + "self": "http://10.3.150.25/identity/v3/limits/3229b3849f584faea483d6851f7aab05" + }, + "service_id": "9408080f1970482aa0e38bc2d4ea34b7", + "project_id": "3a705b9f56bb439381b43c4fe59dccce", + "id": "3229b3849f584faea483d6851f7aab05", + "resource_limit": 5, + "description": "Number of snapshots for project 3a705b9f56bb439381b43c4fe59dccce" + } +} +` + // Model is the enforcement model in the GetEnforcementModel request. var Model = limits.EnforcementModel{ Name: "flat", @@ -130,6 +156,20 @@ var SecondLimit = limits.Limit{ ResourceLimit: 5, } +// SecondLimitUpdated is the updated limit in the Update request. +var SecondLimitUpdated = limits.Limit{ + ResourceName: "snapshot", + RegionID: "RegionOne", + Links: map[string]interface{}{ + "self": "http://10.3.150.25/identity/v3/limits/3229b3849f584faea483d6851f7aab05", + }, + ServiceID: "9408080f1970482aa0e38bc2d4ea34b7", + ProjectID: "3a705b9f56bb439381b43c4fe59dccce", + ID: "3229b3849f584faea483d6851f7aab05", + ResourceLimit: 5, + Description: "Number of snapshots for project 3a705b9f56bb439381b43c4fe59dccce", +} + // ExpectedLimitsSlice is the slice of limits expected to be returned from ListOutput. var ExpectedLimitsSlice = []limits.Limit{FirstLimit, SecondLimit} @@ -187,3 +227,16 @@ func HandleGetLimitSuccessfully(t *testing.T) { fmt.Fprintf(w, GetOutput) }) } + +// HandleUpdateLimitSuccessfully creates an HTTP handler at `/limits` on the +// test handler mux that tests limit update. +func HandleUpdateLimitSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/limits/3229b3849f584faea483d6851f7aab05", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PATCH") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + th.TestJSONRequest(t, r, UpdateRequest) + + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, UpdateOutput) + }) +} diff --git a/openstack/identity/v3/limits/testing/requests_test.go b/openstack/identity/v3/limits/testing/requests_test.go index 90d08ad13a..4c1bd0815d 100644 --- a/openstack/identity/v3/limits/testing/requests_test.go +++ b/openstack/identity/v3/limits/testing/requests_test.go @@ -87,3 +87,20 @@ func TestGetLimit(t *testing.T) { th.AssertNoErr(t, err) th.CheckDeepEquals(t, FirstLimit, *actual) } + +func TestUpdateLimit(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleUpdateLimitSuccessfully(t) + + var description = "Number of snapshots for project 3a705b9f56bb439381b43c4fe59dccce" + var resourceLimit = 5 + updateOpts := limits.UpdateOpts{ + Description: &description, + ResourceLimit: &resourceLimit, + } + + actual, err := limits.Update(client.ServiceClient(), "3229b3849f584faea483d6851f7aab05", updateOpts).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, SecondLimitUpdated, *actual) +} From 44ddb1902301c2f6c84891b27b72af5f8aa8e9e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= <m.andre@redhat.com> Date: Wed, 17 May 2023 12:52:06 +0200 Subject: [PATCH 238/360] Pin goimport dep to a version that works with go 1.14 --- .github/workflows/unit.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/unit.yml b/.github/workflows/unit.yml index 9824f624ca..9a49c86d26 100644 --- a/.github/workflows/unit.yml +++ b/.github/workflows/unit.yml @@ -31,10 +31,11 @@ jobs: run: | # Changing into a different directory to avoid polluting go.sum with "go get" cd "$(mktemp -d)" + go mod init unit_tests # we use "go get" for Go v1.14 go install github.com/wadey/gocovmerge@master || go get github.com/wadey/gocovmerge - go install golang.org/x/tools/cmd/goimports@latest || go get golang.org/x/tools/cmd/goimports + go install golang.org/x/tools/cmd/goimports@latest || go get golang.org/x/tools/cmd/goimports@v0.8.0 - name: Run go vet run: | From d2075f0c660ee3850fe0e0bc7b935dd66ab8192e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= <m.andre@redhat.com> Date: Wed, 17 May 2023 10:54:39 +0200 Subject: [PATCH 239/360] Consistently set OS_BRANCH in acceptance tests A couple of functions rely on this environment variable being set. --- .github/workflows/functional-basic.yaml | 1 + .github/workflows/functional-clustering.yaml | 1 + .github/workflows/functional-compute.yaml | 1 + .github/workflows/functional-containerinfra.yaml | 1 + .github/workflows/functional-dns.yaml | 1 + .github/workflows/functional-identity.yaml | 1 + .github/workflows/functional-imageservice.yaml | 1 + .github/workflows/functional-keymanager.yaml | 1 + .github/workflows/functional-messaging.yaml | 1 + .github/workflows/functional-objectstorage.yaml | 1 + .github/workflows/functional-orchestration.yaml | 1 + .github/workflows/functional-placement.yaml | 1 + .github/workflows/functional-sharedfilesystems.yaml | 1 + 13 files changed, 13 insertions(+) diff --git a/.github/workflows/functional-basic.yaml b/.github/workflows/functional-basic.yaml index d013fe11c8..727fb9d1e0 100644 --- a/.github/workflows/functional-basic.yaml +++ b/.github/workflows/functional-basic.yaml @@ -51,6 +51,7 @@ jobs: env: DEVSTACK_PATH: ${{ github.workspace }}/devstack ACCEPTANCE_TESTS_FILTER: '^acceptance/openstack$' + OS_BRANCH: ${{ matrix.openstack_version }} - name: Generate logs on failure run: ./script/collectlogs if: failure() diff --git a/.github/workflows/functional-clustering.yaml b/.github/workflows/functional-clustering.yaml index 8c0eda5d6d..110920487b 100644 --- a/.github/workflows/functional-clustering.yaml +++ b/.github/workflows/functional-clustering.yaml @@ -51,6 +51,7 @@ jobs: env: DEVSTACK_PATH: ${{ github.workspace }}/devstack ACCEPTANCE_TESTS_FILTER: "^.*clustering.*$" + OS_BRANCH: ${{ matrix.openstack_version }} - name: Generate logs on failure run: ./script/collectlogs if: failure() diff --git a/.github/workflows/functional-compute.yaml b/.github/workflows/functional-compute.yaml index 9085d04664..188064e4dc 100644 --- a/.github/workflows/functional-compute.yaml +++ b/.github/workflows/functional-compute.yaml @@ -49,6 +49,7 @@ jobs: env: DEVSTACK_PATH: ${{ github.workspace }}/devstack ACCEPTANCE_TESTS_FILTER: "^.*compute.*$" + OS_BRANCH: ${{ matrix.openstack_version }} - name: Generate logs on failure run: ./script/collectlogs if: failure() diff --git a/.github/workflows/functional-containerinfra.yaml b/.github/workflows/functional-containerinfra.yaml index 3f3132f96d..4f1aca8db3 100644 --- a/.github/workflows/functional-containerinfra.yaml +++ b/.github/workflows/functional-containerinfra.yaml @@ -68,6 +68,7 @@ jobs: env: DEVSTACK_PATH: ${{ github.workspace }}/devstack ACCEPTANCE_TESTS_FILTER: "^.*containerinfra.*$" + OS_BRANCH: ${{ matrix.openstack_version }} - name: Generate logs on failure run: ./script/collectlogs if: failure() diff --git a/.github/workflows/functional-dns.yaml b/.github/workflows/functional-dns.yaml index bf5164d370..ec9b829d96 100644 --- a/.github/workflows/functional-dns.yaml +++ b/.github/workflows/functional-dns.yaml @@ -63,6 +63,7 @@ jobs: env: DEVSTACK_PATH: ${{ github.workspace }}/devstack ACCEPTANCE_TESTS_FILTER: "^acceptance/openstack/dns.*$" + OS_BRANCH: ${{ matrix.openstack_version }} - name: Generate logs on failure run: ./script/collectlogs if: failure() diff --git a/.github/workflows/functional-identity.yaml b/.github/workflows/functional-identity.yaml index 478ac30a84..9cf1a44b8b 100644 --- a/.github/workflows/functional-identity.yaml +++ b/.github/workflows/functional-identity.yaml @@ -47,6 +47,7 @@ jobs: env: DEVSTACK_PATH: ${{ github.workspace }}/devstack ACCEPTANCE_TESTS_FILTER: "^.*identity.*$" + OS_BRANCH: ${{ matrix.openstack_version }} - name: Generate logs on failure run: ./script/collectlogs if: failure() diff --git a/.github/workflows/functional-imageservice.yaml b/.github/workflows/functional-imageservice.yaml index b60802b9d5..7b1c3fefd9 100644 --- a/.github/workflows/functional-imageservice.yaml +++ b/.github/workflows/functional-imageservice.yaml @@ -47,6 +47,7 @@ jobs: env: DEVSTACK_PATH: ${{ github.workspace }}/devstack ACCEPTANCE_TESTS_FILTER: "^.*imageservice.*$" + OS_BRANCH: ${{ matrix.openstack_version }} - name: Generate logs on failure run: ./script/collectlogs if: failure() diff --git a/.github/workflows/functional-keymanager.yaml b/.github/workflows/functional-keymanager.yaml index 932de23c1b..cd270e7416 100644 --- a/.github/workflows/functional-keymanager.yaml +++ b/.github/workflows/functional-keymanager.yaml @@ -50,6 +50,7 @@ jobs: env: DEVSTACK_PATH: ${{ github.workspace }}/devstack ACCEPTANCE_TESTS_FILTER: "^.*keymanager.*$" + OS_BRANCH: ${{ matrix.openstack_version }} - name: Generate logs on failure run: ./script/collectlogs if: failure() diff --git a/.github/workflows/functional-messaging.yaml b/.github/workflows/functional-messaging.yaml index 3ab2f58f7d..b727eafbce 100644 --- a/.github/workflows/functional-messaging.yaml +++ b/.github/workflows/functional-messaging.yaml @@ -50,6 +50,7 @@ jobs: env: DEVSTACK_PATH: ${{ github.workspace }}/devstack ACCEPTANCE_TESTS_FILTER: "^.*messaging.*$" + OS_BRANCH: ${{ matrix.openstack_version }} - name: Generate logs on failure run: ./script/collectlogs if: failure() diff --git a/.github/workflows/functional-objectstorage.yaml b/.github/workflows/functional-objectstorage.yaml index 424daacea5..f43f6cad8a 100644 --- a/.github/workflows/functional-objectstorage.yaml +++ b/.github/workflows/functional-objectstorage.yaml @@ -54,6 +54,7 @@ jobs: env: DEVSTACK_PATH: ${{ github.workspace }}/devstack ACCEPTANCE_TESTS_FILTER: '^.*objectstorage.*$' + OS_BRANCH: ${{ matrix.openstack_version }} - name: Generate logs on failure run: ./script/collectlogs if: failure() diff --git a/.github/workflows/functional-orchestration.yaml b/.github/workflows/functional-orchestration.yaml index e8321da433..12dbe82a63 100644 --- a/.github/workflows/functional-orchestration.yaml +++ b/.github/workflows/functional-orchestration.yaml @@ -50,6 +50,7 @@ jobs: env: DEVSTACK_PATH: ${{ github.workspace }}/devstack ACCEPTANCE_TESTS_FILTER: ^.*orchestration.*$ + OS_BRANCH: ${{ matrix.openstack_version }} - name: Generate logs on failure run: ./script/collectlogs if: failure() diff --git a/.github/workflows/functional-placement.yaml b/.github/workflows/functional-placement.yaml index 02d12b386e..57d5990b0a 100644 --- a/.github/workflows/functional-placement.yaml +++ b/.github/workflows/functional-placement.yaml @@ -47,6 +47,7 @@ jobs: env: DEVSTACK_PATH: ${{ github.workspace }}/devstack ACCEPTANCE_TESTS_FILTER: ^.*placement.*$ + OS_BRANCH: ${{ matrix.openstack_version }} - name: Generate logs on failure run: ./script/collectlogs if: failure() diff --git a/.github/workflows/functional-sharedfilesystems.yaml b/.github/workflows/functional-sharedfilesystems.yaml index 62d2d85f1c..b72c33e75c 100644 --- a/.github/workflows/functional-sharedfilesystems.yaml +++ b/.github/workflows/functional-sharedfilesystems.yaml @@ -63,6 +63,7 @@ jobs: env: DEVSTACK_PATH: ${{ github.workspace }}/devstack ACCEPTANCE_TESTS_FILTER: "^.*sharedfilesystems.*$" + OS_BRANCH: ${{ matrix.openstack_version }} - name: Generate logs on failure run: ./script/collectlogs if: failure() From 72f853b57a2db4d1851ef7cba33063147eab6735 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= <m.andre@redhat.com> Date: Wed, 17 May 2023 11:09:32 +0200 Subject: [PATCH 240/360] Require OS_BRANCH variable to be set for tests that need it We want the tests to fail when the required `OS_BRANCH` environment variable is not set. --- acceptance/clients/conditions.go | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/acceptance/clients/conditions.go b/acceptance/clients/conditions.go index e237143be9..bfc67f3186 100644 --- a/acceptance/clients/conditions.go +++ b/acceptance/clients/conditions.go @@ -74,10 +74,19 @@ func RequireIronicHTTPBasic(t *testing.T) { } } +func getReleaseFromEnv(t *testing.T) string { + current_branch := os.Getenv("OS_BRANCH") + if current_branch == "" { + t.Fatal("this test requires OS_BRANCH to be set but it wasn't") + } + return current_branch +} + // SkipRelease will have the test be skipped on a certain // release. Releases are named such as 'stable/mitaka', master, etc. func SkipRelease(t *testing.T, release string) { - if os.Getenv("OS_BRANCH") == release { + current_branch := getReleaseFromEnv(t) + if current_branch == release { t.Skipf("this is not supported in %s", release) } } @@ -85,7 +94,7 @@ func SkipRelease(t *testing.T, release string) { // SkipReleasesBelow will have the test be skipped on releases below a certain // one. Releases are named such as 'stable/mitaka', master, etc. func SkipReleasesBelow(t *testing.T, release string) { - current_branch := os.Getenv("OS_BRANCH") + current_branch := getReleaseFromEnv(t) if IsReleasesBelow(t, release) { t.Skipf("this is not supported below %s, testing in %s", release, current_branch) @@ -96,7 +105,7 @@ func SkipReleasesBelow(t *testing.T, release string) { // one. The test is always skipped on master release. Releases are named such // as 'stable/mitaka', master, etc. func SkipReleasesAbove(t *testing.T, release string) { - current_branch := os.Getenv("OS_BRANCH") + current_branch := getReleaseFromEnv(t) // Assume master is always too new if IsReleasesAbove(t, release) { @@ -108,7 +117,7 @@ func SkipReleasesAbove(t *testing.T, release string) { // one. The result is always true on master release. Releases are named such // as 'stable/mitaka', master, etc. func IsReleasesAbove(t *testing.T, release string) bool { - current_branch := os.Getenv("OS_BRANCH") + current_branch := getReleaseFromEnv(t) // Assume master is always too new if current_branch == "master" || current_branch > release { @@ -121,7 +130,7 @@ func IsReleasesAbove(t *testing.T, release string) bool { // IsReleasesBelow will return true on releases below a certain // one. Releases are named such as 'stable/mitaka', master, etc. func IsReleasesBelow(t *testing.T, release string) bool { - current_branch := os.Getenv("OS_BRANCH") + current_branch := getReleaseFromEnv(t) if current_branch != "master" && current_branch < release { return true From 23b9b15f17745e7c9776629af71112c114fcff9e Mon Sep 17 00:00:00 2001 From: Abhishek Kekane <akekane@redhat.com> Date: Wed, 10 May 2023 00:12:51 +0000 Subject: [PATCH 241/360] Add CRUD support register limit APIs --- acceptance/clients/conditions.go | 7 + .../identity/v3/registeredlimits_test.go | 102 ++++++++ openstack/identity/v3/registeredlimits/doc.go | 81 +++++++ .../identity/v3/registeredlimits/requests.go | 160 +++++++++++++ .../identity/v3/registeredlimits/results.go | 144 ++++++++++++ .../v3/registeredlimits/testing/fixtures.go | 219 ++++++++++++++++++ .../registeredlimits/testing/requests_test.go | 104 +++++++++ .../identity/v3/registeredlimits/urls.go | 20 ++ 8 files changed, 837 insertions(+) create mode 100644 acceptance/openstack/identity/v3/registeredlimits_test.go create mode 100644 openstack/identity/v3/registeredlimits/doc.go create mode 100644 openstack/identity/v3/registeredlimits/requests.go create mode 100644 openstack/identity/v3/registeredlimits/results.go create mode 100644 openstack/identity/v3/registeredlimits/testing/fixtures.go create mode 100644 openstack/identity/v3/registeredlimits/testing/requests_test.go create mode 100644 openstack/identity/v3/registeredlimits/urls.go diff --git a/acceptance/clients/conditions.go b/acceptance/clients/conditions.go index e237143be9..f7772e661d 100644 --- a/acceptance/clients/conditions.go +++ b/acceptance/clients/conditions.go @@ -5,6 +5,13 @@ import ( "testing" ) +// RequiredSystemScope will restrict a test to only be run by system scope. +func RequiredSystemScope(t *testing.T) { + if os.Getenv("OS_SYSTEM_SCOPE") != "all" { + t.Skip("must use system scope to run this test") + } +} + // RequireAdmin will restrict a test to only be run by admin users. func RequireAdmin(t *testing.T) { if os.Getenv("OS_USERNAME") != "admin" { diff --git a/acceptance/openstack/identity/v3/registeredlimits_test.go b/acceptance/openstack/identity/v3/registeredlimits_test.go new file mode 100644 index 0000000000..8fd24f4a5a --- /dev/null +++ b/acceptance/openstack/identity/v3/registeredlimits_test.go @@ -0,0 +1,102 @@ +//go:build acceptance +// +build acceptance + +package v3 + +import ( + "os" + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/identity/v3/registeredlimits" + "github.com/gophercloud/gophercloud/openstack/identity/v3/services" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestRegisteredLimitsCRUD(t *testing.T) { + err := os.Setenv("OS_SYSTEM_SCOPE", "all") + th.AssertNoErr(t, err) + defer os.Unsetenv("OS_SYSTEM_SCOPE") + + clients.RequireAdmin(t) + + client, err := clients.NewIdentityV3Client() + th.AssertNoErr(t, err) + + // Get glance service to register the limit + allServicePages, err := services.List(client, nil).AllPages() + th.AssertNoErr(t, err) + + svList, err := services.ExtractServices(allServicePages) + serviceID := "" + for _, service := range svList { + serviceID = service.ID + break + } + th.AssertIntGreaterOrEqual(t, len(serviceID), 1) + + // Create RegisteredLimit + limitDescription := tools.RandomString("TESTLIMITS-DESC-", 8) + defaultLimit := tools.RandomInt(1, 100) + resourceName := tools.RandomString("LIMIT-NAME-", 8) + + createOpts := registeredlimits.BatchCreateOpts{ + registeredlimits.CreateOpts{ + ServiceID: serviceID, + ResourceName: resourceName, + DefaultLimit: defaultLimit, + Description: limitDescription, + RegionID: "RegionOne", + }, + } + + createdRegisteredLimits, err := registeredlimits.BatchCreate(client, createOpts).Extract() + th.AssertNoErr(t, err) + tools.PrintResource(t, createdRegisteredLimits[0]) + th.AssertIntGreaterOrEqual(t, 1, len(createdRegisteredLimits)) + th.AssertEquals(t, limitDescription, createdRegisteredLimits[0].Description) + th.AssertEquals(t, defaultLimit, createdRegisteredLimits[0].DefaultLimit) + th.AssertEquals(t, resourceName, createdRegisteredLimits[0].ResourceName) + th.AssertEquals(t, serviceID, createdRegisteredLimits[0].ServiceID) + th.AssertEquals(t, "RegionOne", createdRegisteredLimits[0].RegionID) + + // List the registered limits + listOpts := registeredlimits.ListOpts{} + allPages, err := registeredlimits.List(client, listOpts).AllPages() + th.AssertNoErr(t, err) + + _, err = registeredlimits.ExtractRegisteredLimits(allPages) + th.AssertNoErr(t, err) + + // Get RegisteredLimit by ID + registered_limit, err := registeredlimits.Get(client, createdRegisteredLimits[0].ID).Extract() + th.AssertNoErr(t, err) + tools.PrintResource(t, registered_limit) + + // Update the existing registered_limit + updatedDescription := "Test description for registered limit" + updatedDefaultLimit := 1000 + updatedResourceName := tools.RandomString("LIMIT-NAME-", 8) + updatedOpts := registeredlimits.UpdateOpts{ + Description: &updatedDescription, + DefaultLimit: &updatedDefaultLimit, + ServiceID: serviceID, + ResourceName: updatedResourceName, + } + + updated_registered_limit, err := registeredlimits.Update(client, createdRegisteredLimits[0].ID, updatedOpts).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, updated_registered_limit) + th.AssertEquals(t, updated_registered_limit.Description, updatedDescription) + th.AssertEquals(t, updated_registered_limit.DefaultLimit, updatedDefaultLimit) + th.AssertEquals(t, updated_registered_limit.ResourceName, updatedResourceName) + + // Delete the registered limit + del_err := registeredlimits.Delete(client, createdRegisteredLimits[0].ID).ExtractErr() + th.AssertNoErr(t, del_err) + + _, err = registeredlimits.Get(client, createdRegisteredLimits[0].ID).Extract() + th.AssertErr(t, err) +} diff --git a/openstack/identity/v3/registeredlimits/doc.go b/openstack/identity/v3/registeredlimits/doc.go new file mode 100644 index 0000000000..a8cb373d7f --- /dev/null +++ b/openstack/identity/v3/registeredlimits/doc.go @@ -0,0 +1,81 @@ +/* +Package registeredlimits provides information and interaction with registered limits for the +Openstack Identity service. + +Example to List RegisteredLimits + + listOpts := registeredlimits.ListOpts{ + ResourceName: "image_size_total", + } + + allPages, err := registeredlimits.List(identityClient, listOpts).AllPages() + if err != nil { + panic(err) + } + + allLimits, err := limits.ExtractLimits(allPages) + if err != nil { + panic(err) + } + +Example to Create a RegisteredLimit + + batchCreateOpts := registeredlimits.BatchCreateOpts{ + registeredlimits.CreateOpts{ + ServiceID: "9408080f1970482aa0e38bc2d4ea34b7", + RegionID: "RegionOne", + ResourceName: "snapshot", + DefaultLimit: 5, + }, + registeredlimits.CreateOpts{ + ServiceID: "9408080f1970482aa0e38bc2d4ea34b7", + RegionID: "RegionOne", + ResourceName: "volume", + DefaultLimit: 10, + Description: "Number of volumes for service 9408080f1970482aa0e38bc2d4ea34b7", + }, + } + + createdRegisteredLimits, err := limits.Create(identityClient, batchCreateOpts).Extract() + if err != nil { + panic(err) + } + +Example to Get a RegisteredLimit + + registeredLimitID := "966b3c7d36a24facaf20b7e458bf2192" + registered_limit, err := registeredlimits.Get(client, registeredLimitID).Extract() + if err != nil { + panic(err) + } + +Example to Update a RegisteredLimit + + Either ServiceID, ResourceName, or RegionID must be different than existing value otherwise it will raise 409. + + registeredLimitID := "966b3c7d36a24facaf20b7e458bf2192" + + resourceName := "images" + description := "Number of images for service 9408080f1970482aa0e38bc2d4ea34b7" + defaultLimit := 10 + updateOpts := registeredlimits.UpdateOpts{ + Description: &description, + DefaultLimit: &defaultLimit, + ResourceName: resourceName, + ServiceID: "9408080f1970482aa0e38bc2d4ea34b7", + } + + registered_limit, err := registeredlimits.Update(client, registeredLimitID, updateOpts).Extract() + if err != nil { + panic(err) + } + +Example to Delete a RegisteredLimit + + registeredLimitID := "966b3c7d36a24facaf20b7e458bf2192" + err := registeredlimits.Delete(identityClient, registeredLimitID).ExtractErr() + if err != nil { + panic(err) + } +*/ +package registeredlimits diff --git a/openstack/identity/v3/registeredlimits/requests.go b/openstack/identity/v3/registeredlimits/requests.go new file mode 100644 index 0000000000..8f6a0fdd30 --- /dev/null +++ b/openstack/identity/v3/registeredlimits/requests.go @@ -0,0 +1,160 @@ +package registeredlimits + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// ListOptsBuilder allows extensions to add additional parameters to +// the List request +type ListOptsBuilder interface { + ToRegisteredLimitListQuery() (string, error) +} + +// ListOpts provides options to filter the List results. +type ListOpts struct { + // Filters the response by a region ID. + RegionID string `q:"region_id"` + + // Filters the response by a service ID. + ServiceID string `q:"service_id"` + + // Filters the response by a resource name. + ResourceName string `q:"resource_name"` +} + +// ToRegisteredLimitListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToRegisteredLimitListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// List enumerates the registered limits. +func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := rootURL(client) + if opts != nil { + query, err := opts.ToRegisteredLimitListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return RegisteredLimitPage{pagination.LinkedPageBase{PageResult: r}} + }) +} + +// BatchCreateOptsBuilder allows extensions to add additional parameters to +// the Create request. +type BatchCreateOptsBuilder interface { + ToRegisteredLimitsCreateMap() (map[string]interface{}, error) +} + +type CreateOpts struct { + // RegionID is the ID of the region where the limit is applied. + RegionID string `json:"region_id,omitempty"` + + // ServiceID is the ID of the service where the limit is applied. + ServiceID string `json:"service_id" required:"true"` + + // Description of the limit. + Description string `json:"description,omitempty"` + + // ResourceName is the name of the resource that the limit is applied to. + ResourceName string `json:"resource_name" required:"true"` + + // DefaultLimit is the default limit. + DefaultLimit int `json:"default_limit" required:"true"` +} + +// BatchCreateOpts provides options used to create limits. +type BatchCreateOpts []CreateOpts + +// ToRegisteredLimitsCreateMap formats a BatchCreateOpts into a create request. +func (opts BatchCreateOpts) ToRegisteredLimitsCreateMap() (map[string]interface{}, error) { + registered_limits := make([]map[string]interface{}, len(opts)) + for i, registered_limit := range opts { + registeredLimitMap, err := registered_limit.ToMap() + if err != nil { + return nil, err + } + registered_limits[i] = registeredLimitMap + } + return map[string]interface{}{"registered_limits": registered_limits}, nil +} + +func (opts CreateOpts) ToMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "") +} + +// BatchCreate creates new Limits. +func BatchCreate(client *gophercloud.ServiceClient, opts BatchCreateOptsBuilder) (r CreateResult) { + b, err := opts.ToRegisteredLimitsCreateMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Post(rootURL(client), &b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{201}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// Get retrieves details on a single registered_limit, by ID. +func Get(client *gophercloud.ServiceClient, registeredLimitID string) (r GetResult) { + resp, err := client.Get(resourceURL(client, registeredLimitID), &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 { + ToRegisteredLimitUpdateMap() (map[string]interface{}, error) +} + +// UpdateOpts represents parameters to update a domain. +type UpdateOpts struct { + // Description of the registered_limit. + Description *string `json:"description,omitempty"` + + // DefaultLimit is the override limit. + DefaultLimit *int `json:"default_limit,omitempty"` + + // RegionID is the ID of the region where the limit is applied. + RegionID string `json:"region_id,omitempty"` + + // ServiceID is the ID of the service where the limit is applied. + ServiceID string `json:"service_id,omitempty"` + + // ResourceName is the name of the resource that the limit is applied to. + ResourceName string `json:"resource_name,omitempty"` + //Either service_id, resource_name, or region_id must be different than existing value otherwise it will raise 409. +} + +// ToRegisteredLimitUpdateMap formats UpdateOpts into an update request. +func (opts UpdateOpts) ToRegisteredLimitUpdateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "registered_limit") +} + +// Update modifies the attributes of a registered limit. +func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { + b, err := opts.ToRegisteredLimitUpdateMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Patch(resourceURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// Delete deletes a registered_limit. +func Delete(client *gophercloud.ServiceClient, registeredLimitID string) (r DeleteResult) { + resp, err := client.Delete(resourceURL(client, registeredLimitID), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} diff --git a/openstack/identity/v3/registeredlimits/results.go b/openstack/identity/v3/registeredlimits/results.go new file mode 100644 index 0000000000..c10707154c --- /dev/null +++ b/openstack/identity/v3/registeredlimits/results.go @@ -0,0 +1,144 @@ +package registeredlimits + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// A model describing the configured enforcement model used by the deployment. +type EnforcementModel struct { + // The name of the enforcement model. + Name string `json:"name"` + + // A short description of the enforcement model used. + Description string `json:"description"` +} + +// EnforcementModelResult is the response from a GetEnforcementModel operation. Call its Extract method +// to interpret it as a EnforcementModel. +type EnforcementModelResult struct { + gophercloud.Result +} + +// Extract interprets EnforcementModelResult as a EnforcementModel. +func (r EnforcementModelResult) Extract() (*EnforcementModel, error) { + var out struct { + Model *EnforcementModel `json:"model"` + } + err := r.ExtractInto(&out) + return out.Model, err +} + +// A registered limit is the limit that is default for all projects. +type RegisteredLimit struct { + // ID is the unique ID of the limit. + ID string `json:"id"` + + // RegionID is the ID of the region where the limit is applied. + RegionID string `json:"region_id"` + + // ServiceID is the ID of the service where the limit is applied. + ServiceID string `json:"service_id"` + + // Description of the limit. + Description string `json:"description"` + + // ResourceName is the name of the resource that the limit is applied to. + ResourceName string `json:"resource_name"` + + // DefaultLimit is the default limit. + DefaultLimit int `json:"default_limit"` + + // Links contains referencing links to the limit. + Links map[string]interface{} `json:"links"` +} + +// A LimitsOutput is an array of limits returned by List and BatchCreate operations +type RegisteredLimitsOutput struct { + RegisteredLimits []RegisteredLimit `json:"registered_limits"` +} + +// A RegisteredLimitOutput is an encapsulated Limit returned by Get and Update operations +type RegisteredLimitOutput struct { + RegisteredLimit *RegisteredLimit `json:"registered_limit"` +} + +// RegisteredLimitPage is a single page of Registered Limit results. +type RegisteredLimitPage struct { + pagination.LinkedPageBase +} + +type commonResult struct { + gophercloud.Result +} + +// GetResult is the response from a Get operation. Call its Extract method +// to interpret it as a RegisteredLimit. +type GetResult struct { + commonResult +} + +// CreateResult is the response from a Create operation. Call its Extract method +// to interpret it as a Registered Limits. +type CreateResult struct { + gophercloud.Result +} + +// UpdateResult is the result of an Update request. Call its Extract method to +// interpret it as a Limit. +type UpdateResult struct { + commonResult +} + +// DeleteResult is the result of a Delete request. Call its ExtractErr method to +// determine if the request succeeded or failed. +type DeleteResult struct { + gophercloud.ErrResult +} + +// IsEmpty determines whether or not a page of Limits contains any results. +func (r RegisteredLimitPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + + registered_limits, err := ExtractRegisteredLimits(r) + return len(registered_limits) == 0, err +} + +// NextPageURL extracts the "next" link from the links section of the result. +func (r RegisteredLimitPage) 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 +} + +// ExtractRegisteredLimits returns a slice of Registered Limits contained in a single page of +// results. +func ExtractRegisteredLimits(r pagination.Page) ([]RegisteredLimit, error) { + var out RegisteredLimitsOutput + err := (r.(RegisteredLimitPage)).ExtractInto(&out) + return out.RegisteredLimits, err +} + +// Extract interprets CreateResult as slice of RegisteredLimits. +func (r CreateResult) Extract() ([]RegisteredLimit, error) { + var out RegisteredLimitsOutput + err := r.ExtractInto(&out) + return out.RegisteredLimits, err +} + +// Extract interprets any commonResult as a RegisteredLimit. +func (r commonResult) Extract() (*RegisteredLimit, error) { + var out RegisteredLimitOutput + err := r.ExtractInto(&out) + return out.RegisteredLimit, err +} diff --git a/openstack/identity/v3/registeredlimits/testing/fixtures.go b/openstack/identity/v3/registeredlimits/testing/fixtures.go new file mode 100644 index 0000000000..4e92bb763e --- /dev/null +++ b/openstack/identity/v3/registeredlimits/testing/fixtures.go @@ -0,0 +1,219 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + "github.com/gophercloud/gophercloud/openstack/identity/v3/registeredlimits" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +// ListOutput provides a single page of List results. +const ListOutput = ` +{ + "links": { + "self": "http://10.3.150.25/identity/v3/registered_limits", + "previous": null, + "next": null + }, + "registered_limits": [ + { + "resource_name": "volume", + "region_id": "RegionOne", + "links": { + "self": "http://10.3.150.25/identity/v3/registered_limits/25a04c7a065c430590881c646cdcdd58" + }, + "service_id": "9408080f1970482aa0e38bc2d4ea34b7", + "id": "25a04c7a065c430590881c646cdcdd58", + "default_limit": 11, + "description": "Number of volumes for service 9408080f1970482aa0e38bc2d4ea34b7" + }, + { + "resource_name": "snapshot", + "region_id": "RegionOne", + "links": { + "self": "http://10.3.150.25/identity/v3/registered_limits/3229b3849f584faea483d6851f7aab05" + }, + "service_id": "9408080f1970482aa0e38bc2d4ea34b7", + "id": "3229b3849f584faea483d6851f7aab05", + "default_limit": 5, + "description": null + } + ] +} +` + +// GetOutput provides a Get result. +const GetOutput = ` +{ + "registered_limit": { + "id": "3229b3849f584faea483d6851f7aab05", + "service_id": "9408080f1970482aa0e38bc2d4ea34b7", + "region_id": "RegionOne", + "resource_name": "snapshot", + "default_limit": 5, + "description": null, + "links": { + "self": "http://10.3.150.25/identity/v3/registered_limits/3229b3849f584faea483d6851f7aab05" + } + } +} +` + +const CreateRequest = ` +{ + "registered_limits":[ + { + "service_id": "9408080f1970482aa0e38bc2d4ea34b7", + "region_id": "RegionOne", + "resource_name": "snapshot", + "default_limit": 5 + }, + { + "service_id": "9408080f1970482aa0e38bc2d4ea34b7", + "region_id": "RegionOne", + "resource_name": "volume", + "default_limit": 11, + "description": "Number of volumes for service 9408080f1970482aa0e38bc2d4ea34b7" + } + ] +} +` + +// UpdateRequest provides the input to an Update request. +const UpdateRequest = ` +{ + "registered_limit": { + "service_id": "9408080f1970482aa0e38bc2d4ea34b7", + "default_limit": 15, + "resource_name": "volumes" + } +} +` + +// UpdateOutput provides an Update response. +const UpdateOutput = ` +{ + "registered_limit": { + "id": "3229b3849f584faea483d6851f7aab05", + "service_id": "9408080f1970482aa0e38bc2d4ea34b7", + "region_id": "RegionOne", + "resource_name": "volumes", + "default_limit": 15, + "description": "Number of volumes for service 9408080f1970482aa0e38bc2d4ea34b7", + "links": { + "self": "http://10.3.150.25/identity/v3/registered_limits/3229b3849f584faea483d6851f7aab05" + } + } +} +` + +const CreateOutput = ListOutput + +// FirstLimit is the first limit in the List request. +var FirstRegisteredLimit = registeredlimits.RegisteredLimit{ + ResourceName: "volume", + RegionID: "RegionOne", + Links: map[string]interface{}{ + "self": "http://10.3.150.25/identity/v3/registered_limits/25a04c7a065c430590881c646cdcdd58", + }, + ServiceID: "9408080f1970482aa0e38bc2d4ea34b7", + ID: "25a04c7a065c430590881c646cdcdd58", + DefaultLimit: 11, + Description: "Number of volumes for service 9408080f1970482aa0e38bc2d4ea34b7", +} + +// SecondLimit is the second limit in the List request. +var SecondRegisteredLimit = registeredlimits.RegisteredLimit{ + ResourceName: "snapshot", + RegionID: "RegionOne", + Links: map[string]interface{}{ + "self": "http://10.3.150.25/identity/v3/registered_limits/3229b3849f584faea483d6851f7aab05", + }, + ServiceID: "9408080f1970482aa0e38bc2d4ea34b7", + ID: "3229b3849f584faea483d6851f7aab05", + DefaultLimit: 5, +} + +// UpdatedSecondRegisteredLimit is a Registered Limit Fixture. +var UpdatedSecondRegisteredLimit = registeredlimits.RegisteredLimit{ + ResourceName: "volumes", + RegionID: "RegionOne", + Links: map[string]interface{}{ + "self": "http://10.3.150.25/identity/v3/registered_limits/3229b3849f584faea483d6851f7aab05", + }, + ServiceID: "9408080f1970482aa0e38bc2d4ea34b7", + ID: "3229b3849f584faea483d6851f7aab05", + DefaultLimit: 15, + Description: "Number of volumes for service 9408080f1970482aa0e38bc2d4ea34b7", +} + +// ExpectedRegisteredLimitsSlice is the slice of registered_limits expected to be returned from ListOutput. +var ExpectedRegisteredLimitsSlice = []registeredlimits.RegisteredLimit{FirstRegisteredLimit, SecondRegisteredLimit} + +// HandleListRegisteredLimitsSuccessfully creates an HTTP handler at `/registered_limits` on the +// test handler mux that responds with a list of two registered limits. +func HandleListRegisteredLimitsSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/registered_limits", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "Accept", "application/json") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, ListOutput) + }) +} + +// HandleGetRegisteredLimitSuccessfully creates an HTTP handler at `/registered_limits` on the +// test handler mux that responds with a single project. +func HandleGetRegisteredLimitSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/registered_limits/3229b3849f584faea483d6851f7aab05", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "Accept", "application/json") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, GetOutput) + }) +} + +// HandleCreateRegisteredLimitSuccessfully creates an HTTP handler at `/registered_limits` on the +// test handler mux that tests registered limit creation. +func HandleCreateRegisteredLimitSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/registered_limits", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + th.TestJSONRequest(t, r, CreateRequest) + + w.WriteHeader(http.StatusCreated) + fmt.Fprintf(w, CreateOutput) + }) +} + +// HandleDeleteRegisteredLimitSuccessfully creates an HTTP handler at `/registered_limits` on the +// test handler mux that tests registered_limit deletion. +func HandleDeleteRegisteredLimitSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/registered_limits/3229b3849f584faea483d6851f7aab05", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "DELETE") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + w.WriteHeader(http.StatusNoContent) + }) +} + +// HandleUpdateRegisteredLimitSuccessfully creates an HTTP handler at `/registered_limits` on the +// test handler mux that tests registered limits updates. +func HandleUpdateRegisteredLimitSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/registered_limits/3229b3849f584faea483d6851f7aab05", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PATCH") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + th.TestJSONRequest(t, r, UpdateRequest) + + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, UpdateOutput) + }) +} diff --git a/openstack/identity/v3/registeredlimits/testing/requests_test.go b/openstack/identity/v3/registeredlimits/testing/requests_test.go new file mode 100644 index 0000000000..b4476176ee --- /dev/null +++ b/openstack/identity/v3/registeredlimits/testing/requests_test.go @@ -0,0 +1,104 @@ +package testing + +import ( + "testing" + + "github.com/gophercloud/gophercloud/openstack/identity/v3/registeredlimits" + "github.com/gophercloud/gophercloud/pagination" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestListRegisteredLimits(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListRegisteredLimitsSuccessfully(t) + + count := 0 + err := registeredlimits.List(client.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) { + count++ + + actual, err := registeredlimits.ExtractRegisteredLimits(page) + th.AssertNoErr(t, err) + + th.CheckDeepEquals(t, ExpectedRegisteredLimitsSlice, actual) + + return true, nil + }) + th.AssertNoErr(t, err) + th.CheckEquals(t, count, 1) +} + +func TestListRegisteredLimitsAllPages(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListRegisteredLimitsSuccessfully(t) + + allPages, err := registeredlimits.List(client.ServiceClient(), nil).AllPages() + th.AssertNoErr(t, err) + actual, err := registeredlimits.ExtractRegisteredLimits(allPages) + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, ExpectedRegisteredLimitsSlice, actual) +} + +func TestCreateRegisteredLimits(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleCreateRegisteredLimitSuccessfully(t) + + createOpts := registeredlimits.BatchCreateOpts{ + registeredlimits.CreateOpts{ + ServiceID: "9408080f1970482aa0e38bc2d4ea34b7", + RegionID: "RegionOne", + ResourceName: "snapshot", + DefaultLimit: 5, + }, + registeredlimits.CreateOpts{ + ServiceID: "9408080f1970482aa0e38bc2d4ea34b7", + RegionID: "RegionOne", + ResourceName: "volume", + DefaultLimit: 11, + Description: "Number of volumes for service 9408080f1970482aa0e38bc2d4ea34b7", + }, + } + + actual, err := registeredlimits.BatchCreate(client.ServiceClient(), createOpts).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, ExpectedRegisteredLimitsSlice, actual) +} + +func TestGetRegisteredLimit(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleGetRegisteredLimitSuccessfully(t) + + actual, err := registeredlimits.Get(client.ServiceClient(), "3229b3849f584faea483d6851f7aab05").Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, SecondRegisteredLimit, *actual) +} + +func TestDeleteRegisteredLimit(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleDeleteRegisteredLimitSuccessfully(t) + + res := registeredlimits.Delete(client.ServiceClient(), "3229b3849f584faea483d6851f7aab05") + th.AssertNoErr(t, res.Err) +} + +func TestUpdateRegisteredLimit(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleUpdateRegisteredLimitSuccessfully(t) + + defaultLimit := 15 + updateOpts := registeredlimits.UpdateOpts{ + ServiceID: "9408080f1970482aa0e38bc2d4ea34b7", + ResourceName: "volumes", + DefaultLimit: &defaultLimit, + } + + actual, err := registeredlimits.Update(client.ServiceClient(), "3229b3849f584faea483d6851f7aab05", updateOpts).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, UpdatedSecondRegisteredLimit, *actual) +} diff --git a/openstack/identity/v3/registeredlimits/urls.go b/openstack/identity/v3/registeredlimits/urls.go new file mode 100644 index 0000000000..c08949bcc8 --- /dev/null +++ b/openstack/identity/v3/registeredlimits/urls.go @@ -0,0 +1,20 @@ +package registeredlimits + +import "github.com/gophercloud/gophercloud" + +const ( + rootPath = "registered_limits" + enforcementModelPath = "model" +) + +func enforcementModelURL(client *gophercloud.ServiceClient) string { + return client.ServiceURL(rootPath, enforcementModelPath) +} + +func rootURL(client *gophercloud.ServiceClient) string { + return client.ServiceURL(rootPath) +} + +func resourceURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL(rootPath, id) +} From 710abfc93022060bafdb727b4ec969479f68bac7 Mon Sep 17 00:00:00 2001 From: Emil Maruszczak <emil.maruszczak@thehutgroup.com> Date: Thu, 29 Dec 2022 17:24:25 +0100 Subject: [PATCH 242/360] Add Delete operation --- acceptance/openstack/identity/v3/limits_test.go | 3 +++ openstack/identity/v3/limits/doc.go | 8 ++++++++ openstack/identity/v3/limits/requests.go | 7 +++++++ openstack/identity/v3/limits/results.go | 6 ++++++ openstack/identity/v3/limits/testing/fixtures.go | 11 +++++++++++ openstack/identity/v3/limits/testing/requests_test.go | 9 +++++++++ 6 files changed, 44 insertions(+) diff --git a/acceptance/openstack/identity/v3/limits_test.go b/acceptance/openstack/identity/v3/limits_test.go index 2de8233b7c..54eae5addb 100644 --- a/acceptance/openstack/identity/v3/limits_test.go +++ b/acceptance/openstack/identity/v3/limits_test.go @@ -114,4 +114,7 @@ func TestLimitsCRUD(t *testing.T) { th.AssertNoErr(t, err) th.AssertEquals(t, newLimitDescription, updatedLimit.Description) th.AssertEquals(t, newResourceLimit, updatedLimit.ResourceLimit) + + err = limits.Delete(client, limitID).ExtractErr() + th.AssertNoErr(t, err) } diff --git a/openstack/identity/v3/limits/doc.go b/openstack/identity/v3/limits/doc.go index 2c307636a5..67f9c6f949 100644 --- a/openstack/identity/v3/limits/doc.go +++ b/openstack/identity/v3/limits/doc.go @@ -71,5 +71,13 @@ Example to Update a Limit if err != nil { panic(err) } + +Example to Delete a Limit + + limitID := "0fe36e73809d46aeae6705c39077b1b3" + err := limits.Delete(identityClient, limitID).ExtractErr() + if err != nil { + panic(err) + } */ package limits diff --git a/openstack/identity/v3/limits/requests.go b/openstack/identity/v3/limits/requests.go index dc6bdba187..5540167b9d 100644 --- a/openstack/identity/v3/limits/requests.go +++ b/openstack/identity/v3/limits/requests.go @@ -160,3 +160,10 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } + +// Delete deletes a limit. +func Delete(client *gophercloud.ServiceClient, limitID string) (r DeleteResult) { + resp, err := client.Delete(resourceURL(client, limitID), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} diff --git a/openstack/identity/v3/limits/results.go b/openstack/identity/v3/limits/results.go index f6a1771400..b811a9deb6 100644 --- a/openstack/identity/v3/limits/results.go +++ b/openstack/identity/v3/limits/results.go @@ -96,6 +96,12 @@ type UpdateResult struct { commonResult } +// DeleteResult is the response from a Delete operation. Call its ExtractErr to +// determine if the request succeeded or failed. +type DeleteResult struct { + gophercloud.ErrResult +} + // IsEmpty determines whether or not a page of Limits contains any results. func (r LimitPage) IsEmpty() (bool, error) { if r.StatusCode == 204 { diff --git a/openstack/identity/v3/limits/testing/fixtures.go b/openstack/identity/v3/limits/testing/fixtures.go index ecdeb2ca0d..0b66f8bc76 100644 --- a/openstack/identity/v3/limits/testing/fixtures.go +++ b/openstack/identity/v3/limits/testing/fixtures.go @@ -240,3 +240,14 @@ func HandleUpdateLimitSuccessfully(t *testing.T) { fmt.Fprintf(w, UpdateOutput) }) } + +// HandleDeleteLimitSuccessfully creates an HTTP handler at `/limits` on the +// test handler mux that tests limit deletion. +func HandleDeleteLimitSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/limits/3229b3849f584faea483d6851f7aab05", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "DELETE") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + w.WriteHeader(http.StatusNoContent) + }) +} diff --git a/openstack/identity/v3/limits/testing/requests_test.go b/openstack/identity/v3/limits/testing/requests_test.go index 4c1bd0815d..ac98bfcc58 100644 --- a/openstack/identity/v3/limits/testing/requests_test.go +++ b/openstack/identity/v3/limits/testing/requests_test.go @@ -104,3 +104,12 @@ func TestUpdateLimit(t *testing.T) { th.AssertNoErr(t, err) th.CheckDeepEquals(t, SecondLimitUpdated, *actual) } + +func TestDeleteLimit(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleDeleteLimitSuccessfully(t) + + err := limits.Delete(client.ServiceClient(), "3229b3849f584faea483d6851f7aab05").ExtractErr() + th.AssertNoErr(t, err) +} From 96aaa4a60963abedeef8d9b08a60bf3fea132e4f Mon Sep 17 00:00:00 2001 From: Emil Maruszczak <emil.maruszczak@thehutgroup.com> Date: Wed, 17 May 2023 09:46:28 +0200 Subject: [PATCH 243/360] Assert succesfull deletion --- acceptance/openstack/identity/v3/limits_test.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/acceptance/openstack/identity/v3/limits_test.go b/acceptance/openstack/identity/v3/limits_test.go index 54eae5addb..8296dea79a 100644 --- a/acceptance/openstack/identity/v3/limits_test.go +++ b/acceptance/openstack/identity/v3/limits_test.go @@ -117,4 +117,7 @@ func TestLimitsCRUD(t *testing.T) { err = limits.Delete(client, limitID).ExtractErr() th.AssertNoErr(t, err) + + _, err = limits.Get(client, limitID).Extract() + th.AssertErr(t, err) } From bfbaf660528d07bc1165c82fde4978526569fd05 Mon Sep 17 00:00:00 2001 From: Konstantin Eremin <kod-t@mail.ru> Date: Fri, 19 May 2023 17:46:58 +0300 Subject: [PATCH 244/360] Add the ability to remove ingress/egress policies from fwaas_v2 groups Add RemoveIngressPolicy, RemoveEgressPolicy functions. Add acceptance tests. Add unit tests. --- .../v2/extensions/fwaas_v2/groups_test.go | 42 +++++++- .../v2/extensions/fwaas_v2/groups/requests.go | 41 ++++++- .../fwaas_v2/groups/testing/requests_test.go | 102 +++++++++++++++++- 3 files changed, 179 insertions(+), 6 deletions(-) diff --git a/acceptance/openstack/networking/v2/extensions/fwaas_v2/groups_test.go b/acceptance/openstack/networking/v2/extensions/fwaas_v2/groups_test.go index 8dc8518626..1123e3c217 100644 --- a/acceptance/openstack/networking/v2/extensions/fwaas_v2/groups_test.go +++ b/acceptance/openstack/networking/v2/extensions/fwaas_v2/groups_test.go @@ -24,13 +24,28 @@ func TestGroupCRUD(t *testing.T) { tools.PrintResource(t, createdGroup) + createdRule, err := CreateRule(t, client) + th.AssertNoErr(t, err) + defer DeleteRule(t, client, createdRule.ID) + + tools.PrintResource(t, createdRule) + + createdPolicy, err := CreatePolicy(t, client, createdRule.ID) + th.AssertNoErr(t, err) + defer DeletePolicy(t, client, createdPolicy.ID) + + tools.PrintResource(t, createdPolicy) + groupName := tools.RandomString("TESTACC-", 8) adminStateUp := false description := ("Some firewall group description") + firewall_policy_id := createdPolicy.ID updateOpts := groups.UpdateOpts{ - Name: &groupName, - Description: &description, - AdminStateUp: &adminStateUp, + Name: &groupName, + Description: &description, + AdminStateUp: &adminStateUp, + IngressFirewallPolicyID: &firewall_policy_id, + EgressFirewallPolicyID: &firewall_policy_id, } groupUpdated, err := groups.Update(client, createdGroup.ID, updateOpts).Extract() @@ -42,9 +57,30 @@ func TestGroupCRUD(t *testing.T) { th.AssertEquals(t, groupUpdated.Name, groupName) th.AssertEquals(t, groupUpdated.Description, description) th.AssertEquals(t, groupUpdated.AdminStateUp, adminStateUp) + th.AssertEquals(t, groupUpdated.IngressFirewallPolicyID, firewall_policy_id) + th.AssertEquals(t, groupUpdated.EgressFirewallPolicyID, firewall_policy_id) t.Logf("Updated firewall group %s", groupUpdated.ID) + removeIngressPolicy, err := groups.RemoveIngressPolicy(client, groupUpdated.ID).Extract() + if err != nil { + t.Fatalf("Unable to remove ingress firewall policy from firewall group %s: %v", removeIngressPolicy.ID, err) + } + + th.AssertEquals(t, removeIngressPolicy.IngressFirewallPolicyID, "") + th.AssertEquals(t, removeIngressPolicy.EgressFirewallPolicyID, firewall_policy_id) + + t.Logf("Ingress policy removed from firewall group %s", groupUpdated.ID) + + removeEgressPolicy, err := groups.RemoveEgressPolicy(client, groupUpdated.ID).Extract() + if err != nil { + t.Fatalf("Unable to remove egress firewall policy from firewall group %s: %v", removeEgressPolicy.ID, err) + } + + th.AssertEquals(t, removeEgressPolicy.EgressFirewallPolicyID, "") + + t.Logf("Egress policy removed from firewall group %s", groupUpdated.ID) + allPages, err := groups.List(client, nil).AllPages() th.AssertNoErr(t, err) diff --git a/openstack/networking/v2/extensions/fwaas_v2/groups/requests.go b/openstack/networking/v2/extensions/fwaas_v2/groups/requests.go index 34df7695e3..a795ccf2ab 100644 --- a/openstack/networking/v2/extensions/fwaas_v2/groups/requests.go +++ b/openstack/networking/v2/extensions/fwaas_v2/groups/requests.go @@ -128,7 +128,7 @@ type UpdateOpts struct { Shared *bool `json:"shared,omitempty"` } -// ToFirewallGroupUpdateMap casts a CreateOpts struct to a map. +// ToFirewallGroupUpdateMap casts a UpdateOpts struct to a map. func (opts UpdateOpts) ToFirewallGroupUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "firewall_group") } @@ -146,6 +146,45 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r return } +// Because of fwaas_v2 wait only UUID not string and base updateOpts has omitempty, +// only set nil allows firewall group policies to be unset. +// Two different functions, because can not specify both policy in one function. +// New functions needs new structs without omitempty. +// Separate function for BuildRequestBody is missing due to complication +// of code readability and bulkiness. + +type RemoveIngressPolicyOpts struct { + IngressFirewallPolicyID *string `json:"ingress_firewall_policy_id"` +} + +func RemoveIngressPolicy(c *gophercloud.ServiceClient, id string) (r UpdateResult) { + b, err := gophercloud.BuildRequestBody(RemoveIngressPolicyOpts{IngressFirewallPolicyID: nil}, "firewall_group") + if err != nil { + r.Err = err + return + } + _, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} + +type RemoveEgressPolicyOpts struct { + EgressFirewallPolicyID *string `json:"egress_firewall_policy_id"` +} + +func RemoveEgressPolicy(c *gophercloud.ServiceClient, id string) (r UpdateResult) { + b, err := gophercloud.BuildRequestBody(RemoveEgressPolicyOpts{EgressFirewallPolicyID: nil}, "firewall_group") + if err != nil { + r.Err = err + return + } + _, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} + // Delete will permanently delete a particular firewall group based on its unique ID. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { _, r.Err = c.Delete(resourceURL(c, id), nil) diff --git a/openstack/networking/v2/extensions/fwaas_v2/groups/testing/requests_test.go b/openstack/networking/v2/extensions/fwaas_v2/groups/testing/requests_test.go index 4ca7b4aad8..b4ddaa091f 100644 --- a/openstack/networking/v2/extensions/fwaas_v2/groups/testing/requests_test.go +++ b/openstack/networking/v2/extensions/fwaas_v2/groups/testing/requests_test.go @@ -188,6 +188,7 @@ func TestCreate(t *testing.T) { "a6af1e56-b12b-4733-8f77-49166afd5719" ], "ingress_firewall_policy_id": "e3f11142-3792-454b-8d3e-91ac1bf127b4", + "egress_firewall_policy_id": "43a11f3a-ddac-4129-9469-02b9df26548e", "name": "test" } } @@ -204,7 +205,7 @@ func TestCreate(t *testing.T) { "name": "test", "description": "", "ingress_firewall_policy_id": "e3f11142-3792-454b-8d3e-91ac1bf127b4", - "egress_firewall_policy_id": null, + "egress_firewall_policy_id": "43a11f3a-ddac-4129-9469-02b9df26548e", "admin_state_up": true, "ports": [ "a6af1e56-b12b-4733-8f77-49166afd5719" @@ -221,6 +222,7 @@ func TestCreate(t *testing.T) { Name: "test", Description: "", IngressFirewallPolicyID: "e3f11142-3792-454b-8d3e-91ac1bf127b4", + EgressFirewallPolicyID: "43a11f3a-ddac-4129-9469-02b9df26548e", Ports: []string{ "a6af1e56-b12b-4733-8f77-49166afd5719", }, @@ -264,7 +266,7 @@ func TestUpdate(t *testing.T) { "name": "test", "description": "some information", "ingress_firewall_policy_id": "e3f11142-3792-454b-8d3e-91ac1bf127b4", - "egress_firewall_policy_id": null, + "egress_firewall_policy_id": "43a11f3a-ddac-4129-9469-02b9df26548e", "admin_state_up": true, "ports": [ "a6af1e56-b12b-4733-8f77-49166afd5719", @@ -295,6 +297,102 @@ func TestUpdate(t *testing.T) { th.AssertNoErr(t, err) } +func TestRemoveIngressPolicy(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/fwaas/firewall_groups/6bfb0f10-07f7-4a40-b534-bad4b4ca3428", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PUT") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestHeader(t, r, "Content-Type", "application/json") + th.TestHeader(t, r, "Accept", "application/json") + th.TestJSONRequest(t, r, ` +{ + "firewall_group":{ + "ingress_firewall_policy_id": null + } +} + `) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, ` +{ + "firewall_group": { + "id": "6bfb0f10-07f7-4a40-b534-bad4b4ca3428", + "tenant_id": "9f98fc0e5f944cd1b51798b668dc8778", + "name": "test", + "description": "some information", + "ingress_firewall_policy_id": null, + "egress_firewall_policy_id": "43a11f3a-ddac-4129-9469-02b9df26548e", + "admin_state_up": true, + "ports": [ + "a6af1e56-b12b-4733-8f77-49166afd5719", + "11a58c87-76be-ae7c-a74e-b77fffb88a32" + ], + "status": "ACTIVE", + "shared": false, + "project_id": "9f98fc0e5f944cd1b51798b668dc8778" + } +} + `) + }) + + removeIngressPolicy, err := groups.RemoveIngressPolicy(fake.ServiceClient(), "6bfb0f10-07f7-4a40-b534-bad4b4ca3428").Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, removeIngressPolicy.IngressFirewallPolicyID, "") + th.AssertEquals(t, removeIngressPolicy.EgressFirewallPolicyID, "43a11f3a-ddac-4129-9469-02b9df26548e") +} + +func TestRemoveEgressPolicy(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/fwaas/firewall_groups/6bfb0f10-07f7-4a40-b534-bad4b4ca3428", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PUT") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestHeader(t, r, "Content-Type", "application/json") + th.TestHeader(t, r, "Accept", "application/json") + th.TestJSONRequest(t, r, ` +{ + "firewall_group":{ + "egress_firewall_policy_id": null + } +} + `) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, ` +{ + "firewall_group": { + "id": "6bfb0f10-07f7-4a40-b534-bad4b4ca3428", + "tenant_id": "9f98fc0e5f944cd1b51798b668dc8778", + "name": "test", + "description": "some information", + "ingress_firewall_policy_id": "e3f11142-3792-454b-8d3e-91ac1bf127b4", + "egress_firewall_policy_id": null, + "admin_state_up": true, + "ports": [ + "a6af1e56-b12b-4733-8f77-49166afd5719", + "11a58c87-76be-ae7c-a74e-b77fffb88a32" + ], + "status": "ACTIVE", + "shared": false, + "project_id": "9f98fc0e5f944cd1b51798b668dc8778" + } +} + `) + }) + + removeEgressPolicy, err := groups.RemoveEgressPolicy(fake.ServiceClient(), "6bfb0f10-07f7-4a40-b534-bad4b4ca3428").Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, removeEgressPolicy.IngressFirewallPolicyID, "e3f11142-3792-454b-8d3e-91ac1bf127b4") + th.AssertEquals(t, removeEgressPolicy.EgressFirewallPolicyID, "") +} + func TestDelete(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() From 7097b8938e8bfbe9133720d6ff58b00a16ecd364 Mon Sep 17 00:00:00 2001 From: Matthew Booth <mbooth@redhat.com> Date: Fri, 19 May 2023 17:28:39 +0100 Subject: [PATCH 245/360] neutron: Support trunk_details extension --- .../extensions/trunk_details/trunks_test.go | 123 ++++++++++++++++++ .../v2/extensions/trunk_details/doc.go | 20 +++ .../v2/extensions/trunk_details/results.go | 30 +++++ .../extensions/trunk_details/testing/doc.go | 1 + .../trunk_details/testing/fixtures.go | 52 ++++++++ .../trunk_details/testing/requests_test.go | 44 +++++++ 6 files changed, 270 insertions(+) create mode 100644 acceptance/openstack/networking/v2/extensions/trunk_details/trunks_test.go create mode 100644 openstack/networking/v2/extensions/trunk_details/doc.go create mode 100644 openstack/networking/v2/extensions/trunk_details/results.go create mode 100644 openstack/networking/v2/extensions/trunk_details/testing/doc.go create mode 100644 openstack/networking/v2/extensions/trunk_details/testing/fixtures.go create mode 100644 openstack/networking/v2/extensions/trunk_details/testing/requests_test.go diff --git a/acceptance/openstack/networking/v2/extensions/trunk_details/trunks_test.go b/acceptance/openstack/networking/v2/extensions/trunk_details/trunks_test.go new file mode 100644 index 0000000000..56b759cf32 --- /dev/null +++ b/acceptance/openstack/networking/v2/extensions/trunk_details/trunks_test.go @@ -0,0 +1,123 @@ +//go:build acceptance || trunks +// +build acceptance trunks + +package trunk_details + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + v2 "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2" + v2Trunks "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2/extensions/trunks" + "github.com/gophercloud/gophercloud/openstack/common/extensions" + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/trunk_details" + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/trunks" + "github.com/gophercloud/gophercloud/openstack/networking/v2/ports" + th "github.com/gophercloud/gophercloud/testhelper" +) + +type portWithTrunkDetails struct { + ports.Port + trunk_details.TrunkDetailsExt +} + +func TestListPortWithSubports(t *testing.T) { + client, err := clients.NewNetworkV2Client() + if err != nil { + t.Fatalf("Unable to create a network client: %v", err) + } + + _, err = extensions.Get(client, "trunk-details").Extract() + if err != nil { + t.Skip("This test requires trunk-details Neutron extension") + } + + // Create Network + network, err := v2.CreateNetwork(t, client) + if err != nil { + t.Fatalf("Unable to create network: %v", err) + } + defer v2.DeleteNetwork(t, client, network.ID) + + // Create Subnet + subnet, err := v2.CreateSubnet(t, client, network.ID) + if err != nil { + t.Fatalf("Unable to create subnet: %v", err) + } + defer v2.DeleteSubnet(t, client, subnet.ID) + + // Create port + parentPort, err := v2.CreatePort(t, client, network.ID, subnet.ID) + if err != nil { + t.Fatalf("Unable to create port: %v", err) + } + defer v2.DeletePort(t, client, parentPort.ID) + + subport1, err := v2.CreatePort(t, client, network.ID, subnet.ID) + if err != nil { + t.Fatalf("Unable to create port: %v", err) + } + defer v2.DeletePort(t, client, subport1.ID) + + subport2, err := v2.CreatePort(t, client, network.ID, subnet.ID) + if err != nil { + t.Fatalf("Unable to create port: %v", err) + } + defer v2.DeletePort(t, client, subport2.ID) + + trunk, err := v2Trunks.CreateTrunk(t, client, parentPort.ID, subport1.ID, subport2.ID) + if err != nil { + t.Fatalf("Unable to create trunk: %v", err) + } + defer v2Trunks.DeleteTrunk(t, client, trunk.ID) + + // Test LIST ports with trunk details + allPages, err := ports.List(client, ports.ListOpts{ID: parentPort.ID}).AllPages() + th.AssertNoErr(t, err) + + var allPorts []portWithTrunkDetails + err = ports.ExtractPortsInto(allPages, &allPorts) + th.AssertNoErr(t, err) + + th.AssertEquals(t, 1, len(allPorts)) + port := allPorts[0] + + th.AssertEquals(t, trunk.ID, port.TrunkDetails.TrunkID) + th.AssertEquals(t, 2, len(port.TrunkDetails.SubPorts)) + + // Note that MAC address is not (currently) returned in list queries. We + // exclude it from the comparison here in case it's ever added. MAC + // address is returned in GET queries, so we do assert that in the GET + // test below. + th.AssertDeepEquals(t, trunks.Subport{ + SegmentationID: 1, + SegmentationType: "vlan", + PortID: subport1.ID, + }, port.TrunkDetails.SubPorts[0].Subport) + th.AssertDeepEquals(t, trunks.Subport{ + SegmentationID: 2, + SegmentationType: "vlan", + PortID: subport2.ID, + }, port.TrunkDetails.SubPorts[1].Subport) + + // Test GET port with trunk details + err = ports.Get(client, parentPort.ID).ExtractInto(&port) + th.AssertEquals(t, trunk.ID, port.TrunkDetails.TrunkID) + th.AssertEquals(t, 2, len(port.TrunkDetails.SubPorts)) + th.AssertDeepEquals(t, trunk_details.Subport{ + Subport: trunks.Subport{ + SegmentationID: 1, + SegmentationType: "vlan", + PortID: subport1.ID, + }, + MACAddress: subport1.MACAddress, + }, port.TrunkDetails.SubPorts[0]) + th.AssertDeepEquals(t, trunk_details.Subport{ + Subport: trunks.Subport{ + SegmentationID: 2, + SegmentationType: "vlan", + PortID: subport2.ID, + }, + MACAddress: subport2.MACAddress, + }, port.TrunkDetails.SubPorts[1]) +} diff --git a/openstack/networking/v2/extensions/trunk_details/doc.go b/openstack/networking/v2/extensions/trunk_details/doc.go new file mode 100644 index 0000000000..c24ad592be --- /dev/null +++ b/openstack/networking/v2/extensions/trunk_details/doc.go @@ -0,0 +1,20 @@ +/* +Package trunk_details provides the ability to extend a ports result with +additional information about any trunk and subports associated with the port. + +Example: + + type portExt struct { + ports.Port + trunk_details.TrunkDetailsExt + } + var portExt portExt + + err := ports.Get(networkClient, "2ba3a709-e40e-462c-a541-85e99de589bf").ExtractInto(&portExt) + if err != nil { + panic(err) + } + + fmt.Printf("%+v\n", portExt) +*/ +package trunk_details diff --git a/openstack/networking/v2/extensions/trunk_details/results.go b/openstack/networking/v2/extensions/trunk_details/results.go new file mode 100644 index 0000000000..41142f58d2 --- /dev/null +++ b/openstack/networking/v2/extensions/trunk_details/results.go @@ -0,0 +1,30 @@ +package trunk_details + +import ( + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/trunks" +) + +// TrunkDetailsExt represents additional trunking information returned in a +// ports query. +type TrunkDetailsExt struct { + // trunk_details contains details of any trunk associated with the port + TrunkDetails `json:"trunk_details,omitempty"` +} + +// TrunkDetails contains additional trunking information returned in a +// ports query. +type TrunkDetails struct { + // trunk_id contains the UUID of the trunk + TrunkID string `json:"trunk_id"` + + // sub_ports contains a list of subports associated with the trunk + SubPorts []Subport `json:"sub_ports,omitempty"` +} + +type Subport struct { + trunks.Subport + + // mac_address contains the MAC address of the subport. + // Note that MACAddress may not be returned in list queries + MACAddress string `json:"mac_address,omitempty"` +} diff --git a/openstack/networking/v2/extensions/trunk_details/testing/doc.go b/openstack/networking/v2/extensions/trunk_details/testing/doc.go new file mode 100644 index 0000000000..7603f836a0 --- /dev/null +++ b/openstack/networking/v2/extensions/trunk_details/testing/doc.go @@ -0,0 +1 @@ +package testing diff --git a/openstack/networking/v2/extensions/trunk_details/testing/fixtures.go b/openstack/networking/v2/extensions/trunk_details/testing/fixtures.go new file mode 100644 index 0000000000..6ecb9a4876 --- /dev/null +++ b/openstack/networking/v2/extensions/trunk_details/testing/fixtures.go @@ -0,0 +1,52 @@ +package testing + +// PortWithTrunkDetailsResult represents a raw server response from the +// Neutron API with trunk_details enabled. +// Some fields have been deleted from the response. +const PortWithTrunkDetailsResult = ` +{ + "port": { + "id": "dc3e8758-ee96-402d-94b0-4be5e9396c82", + "name": "test-port-with-subports", + "network_id": "42e996cb-6c9e-4cb1-8665-c62aa1610249", + "tenant_id": "d4aa8944-e8be-4f46-bf93-74331af9c49e", + "mac_address": "fa:16:3e:1f:de:6d", + "admin_state_up": true, + "status": "ACTIVE", + "device_id": "935f1d9c-1888-457e-98d7-cb57405086cf", + "device_owner": "compute:nova", + "fixed_ips": [ + { + "subnet_id": "f7aea11b-a649-4d23-995f-dcd4f2513f7e", + "ip_address": "172.16.0.225" + } + ], + "allowed_address_pairs": [], + "extra_dhcp_opts": [], + "security_groups": [ + "614f6c36-50b8-4dde-ab59-a46783befeec" + ], + "description": "", + "binding:vnic_type": "normal", + "qos_policy_id": null, + "port_security_enabled": true, + "trunk_details": { + "trunk_id": "f170c831-8c55-4ceb-ad13-75eab4a121e5", + "sub_ports": [ + { + "segmentation_id": 100, + "segmentation_type": "vlan", + "port_id": "20c673d8-7f9d-4570-b662-148d9ddcc5bd", + "mac_address": "fa:16:3e:88:29:a0" + } + ] + }, + "ip_allocation": "immediate", + "tags": [], + "created_at": "2023-05-05T10:54:51Z", + "updated_at": "2023-05-05T16:26:01Z", + "revision_number": 4, + "project_id": "d4aa8944-e8be-4f46-bf93-74331af9c49e" + } +} +` diff --git a/openstack/networking/v2/extensions/trunk_details/testing/requests_test.go b/openstack/networking/v2/extensions/trunk_details/testing/requests_test.go new file mode 100644 index 0000000000..ae19dd721e --- /dev/null +++ b/openstack/networking/v2/extensions/trunk_details/testing/requests_test.go @@ -0,0 +1,44 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/trunk_details" + "github.com/gophercloud/gophercloud/openstack/networking/v2/ports" + th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestServerWithUsageExt(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + const portIDFixture = "dc3e8758-ee96-402d-94b0-4be5e9396c82" + + th.Mux.HandleFunc("/ports/"+portIDFixture, func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestHeader(t, r, "Accept", "application/json") + + fmt.Fprint(w, PortWithTrunkDetailsResult) + }) + + var portExt struct { + ports.Port + trunk_details.TrunkDetailsExt + } + + // Extract basic fields. + err := ports.Get(fake.ServiceClient(), portIDFixture).ExtractInto(&portExt) + th.AssertNoErr(t, err) + + th.AssertEquals(t, portExt.TrunkDetails.TrunkID, "f170c831-8c55-4ceb-ad13-75eab4a121e5") + th.AssertEquals(t, len(portExt.TrunkDetails.SubPorts), 1) + subPort := portExt.TrunkDetails.SubPorts[0] + th.AssertEquals(t, subPort.SegmentationID, 100) + th.AssertEquals(t, subPort.SegmentationType, "vlan") + th.AssertEquals(t, subPort.PortID, "20c673d8-7f9d-4570-b662-148d9ddcc5bd") + th.AssertEquals(t, subPort.MACAddress, "fa:16:3e:88:29:a0") +} From 0bda0eff3ae3501f703d6dcde4c227294f270517 Mon Sep 17 00:00:00 2001 From: Abhishek Kekane <akekane@redhat.com> Date: Tue, 23 May 2023 09:13:09 +0000 Subject: [PATCH 246/360] Limits: Fix ToDo to create registered limit and use it Since we have API to create a registered limit, we can use it in the acceptance test to create and then override the same in project. Also simplified fetching service logic since we didn't be depending on glance service anymore. --- .../openstack/identity/v3/limits_test.go | 55 +++++++++++++------ 1 file changed, 39 insertions(+), 16 deletions(-) diff --git a/acceptance/openstack/identity/v3/limits_test.go b/acceptance/openstack/identity/v3/limits_test.go index 8296dea79a..05ef790829 100644 --- a/acceptance/openstack/identity/v3/limits_test.go +++ b/acceptance/openstack/identity/v3/limits_test.go @@ -10,6 +10,7 @@ import ( "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/identity/v3/limits" + "github.com/gophercloud/gophercloud/openstack/identity/v3/registeredlimits" "github.com/gophercloud/gophercloud/openstack/identity/v3/services" th "github.com/gophercloud/gophercloud/testhelper" ) @@ -42,19 +43,10 @@ func TestLimitsList(t *testing.T) { } func TestLimitsCRUD(t *testing.T) { - // TODO: After https://github.com/gophercloud/gophercloud/issues/2290 is implemented - // use registered limits API to create global registered limit and then overwrite it with limit. - // Current solution (using glance limit) only works with Openstack Xena and above. - clients.SkipReleasesBelow(t, "stable/xena") - err := os.Setenv("OS_SYSTEM_SCOPE", "all") th.AssertNoErr(t, err) defer os.Unsetenv("OS_SYSTEM_SCOPE") - limitDescription := tools.RandomString("TESTLIMITS-DESC-", 8) - resourceLimit := tools.RandomInt(1, 100) - resourceName := "image_size_total" - clients.RequireAdmin(t) client, err := clients.NewIdentityV3Client() @@ -63,25 +55,47 @@ func TestLimitsCRUD(t *testing.T) { project, err := CreateProject(t, client, nil) th.AssertNoErr(t, err) - // Find image service (glance on Devstack) which has precreated registered limits. + // Get the service to register the limit against. allPages, err := services.List(client, nil).AllPages() th.AssertNoErr(t, err) svList, err := services.ExtractServices(allPages) serviceID := "" for _, service := range svList { - if service.Type == "image" { - serviceID = service.ID - break - } + serviceID = service.ID + break } th.AssertIntGreaterOrEqual(t, len(serviceID), 1) + // Create global registered limit + description := tools.RandomString("GLOBALLIMIT-DESC-", 8) + defaultLimit := tools.RandomInt(1, 100) + globalResourceName := tools.RandomString("GLOBALLIMIT-", 8) + + createRegisteredLimitsOpts := registeredlimits.BatchCreateOpts{ + registeredlimits.CreateOpts{ + ServiceID: serviceID, + ResourceName: globalResourceName, + DefaultLimit: defaultLimit, + Description: description, + RegionID: "RegionOne", + }, + } + + createdRegisteredLimits, err := registeredlimits.BatchCreate(client, createRegisteredLimitsOpts).Extract() + th.AssertNoErr(t, err) + tools.PrintResource(t, createdRegisteredLimits[0]) + th.AssertIntGreaterOrEqual(t, 1, len(createdRegisteredLimits)) + + // Override global limit in specific project + limitDescription := tools.RandomString("TESTLIMITS-DESC-", 8) + resourceLimit := tools.RandomInt(1, 1000) + createOpts := limits.BatchCreateOpts{ limits.CreateOpts{ ServiceID: serviceID, ProjectID: project.ID, - ResourceName: resourceName, + ResourceName: globalResourceName, ResourceLimit: resourceLimit, Description: limitDescription, RegionID: "RegionOne", @@ -93,7 +107,7 @@ func TestLimitsCRUD(t *testing.T) { th.AssertIntGreaterOrEqual(t, 1, len(createdLimits)) th.AssertEquals(t, limitDescription, createdLimits[0].Description) th.AssertEquals(t, resourceLimit, createdLimits[0].ResourceLimit) - th.AssertEquals(t, resourceName, createdLimits[0].ResourceName) + th.AssertEquals(t, globalResourceName, createdLimits[0].ResourceName) th.AssertEquals(t, serviceID, createdLimits[0].ServiceID) th.AssertEquals(t, project.ID, createdLimits[0].ProjectID) @@ -115,9 +129,18 @@ func TestLimitsCRUD(t *testing.T) { th.AssertEquals(t, newLimitDescription, updatedLimit.Description) th.AssertEquals(t, newResourceLimit, updatedLimit.ResourceLimit) + // Verify Deleting registered limit fails as it has project specific limit associated with it + del_err := registeredlimits.Delete(client, createdRegisteredLimits[0].ID).ExtractErr() + th.AssertErr(t, del_err) + + // Delete project specific limit err = limits.Delete(client, limitID).ExtractErr() th.AssertNoErr(t, err) _, err = limits.Get(client, limitID).Extract() th.AssertErr(t, err) + + // Delete registered limit + err = registeredlimits.Delete(client, createdRegisteredLimits[0].ID).ExtractErr() + th.AssertNoErr(t, err) } From e448e8987a92ad235f5dcf6a91bb8b90c6ab228a Mon Sep 17 00:00:00 2001 From: Matthew Booth <mbooth@redhat.com> Date: Wed, 24 May 2023 08:39:49 +0100 Subject: [PATCH 247/360] Add upstream neutron bug for trunk_details --- .../networking/v2/extensions/trunk_details/trunks_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/acceptance/openstack/networking/v2/extensions/trunk_details/trunks_test.go b/acceptance/openstack/networking/v2/extensions/trunk_details/trunks_test.go index 56b759cf32..309d090ffa 100644 --- a/acceptance/openstack/networking/v2/extensions/trunk_details/trunks_test.go +++ b/acceptance/openstack/networking/v2/extensions/trunk_details/trunks_test.go @@ -89,6 +89,8 @@ func TestListPortWithSubports(t *testing.T) { // exclude it from the comparison here in case it's ever added. MAC // address is returned in GET queries, so we do assert that in the GET // test below. + // Tracked in https://bugs.launchpad.net/neutron/+bug/2020552 + // TODO: Remove this workaround when the bug is resolved th.AssertDeepEquals(t, trunks.Subport{ SegmentationID: 1, SegmentationType: "vlan", From 2e1ab9b4fa03dc677c5d3846b2f990d9ef6ebb9e Mon Sep 17 00:00:00 2001 From: kayrus <kay.diam@gmail.com> Date: Wed, 24 May 2023 15:32:08 +0200 Subject: [PATCH 248/360] [manila]: Add share from snapshot restore functional test --- .../openstack/sharedfilesystems/v2/shares.go | 24 +++-- .../sharedfilesystems/v2/shares_test.go | 89 ++++++++++++++++--- 2 files changed, 94 insertions(+), 19 deletions(-) diff --git a/acceptance/openstack/sharedfilesystems/v2/shares.go b/acceptance/openstack/sharedfilesystems/v2/shares.go index 33c1c3ac19..9179a30cf1 100644 --- a/acceptance/openstack/sharedfilesystems/v2/shares.go +++ b/acceptance/openstack/sharedfilesystems/v2/shares.go @@ -14,18 +14,22 @@ import ( // CreateShare will create a share with a name, and a size of 1Gb. An // error will be returned if the share could not be created -func CreateShare(t *testing.T, client *gophercloud.ServiceClient) (*shares.Share, error) { +func CreateShare(t *testing.T, client *gophercloud.ServiceClient, optShareType ...string) (*shares.Share, error) { if testing.Short() { t.Skip("Skipping test that requires share creation in short mode.") } iTrue := true + shareType := "dhss_false" + if len(optShareType) > 0 { + shareType = optShareType[0] + } createOpts := shares.CreateOpts{ Size: 1, Name: "My Test Share", Description: "My Test Description", ShareProto: "NFS", - ShareType: "dhss_false", + ShareType: shareType, IsPublic: &iTrue, } @@ -35,7 +39,7 @@ func CreateShare(t *testing.T, client *gophercloud.ServiceClient) (*shares.Share return nil, err } - err = waitForStatus(t, client, share.ID, "available") + _, err = waitForStatus(t, client, share.ID, "available") if err != nil { t.Logf("Failed to get %s share status", share.ID) DeleteShare(t, client, share) @@ -91,7 +95,7 @@ func DeleteShare(t *testing.T, client *gophercloud.ServiceClient, share *shares. t.Errorf("Unable to delete share %s: %v", share.ID, err) } - err = waitForStatus(t, client, share.ID, "deleted") + _, err = waitForStatus(t, client, share.ID, "deleted") if err != nil { t.Errorf("Failed to wait for 'deleted' status for %s share: %v", share.ID, err) } else { @@ -129,9 +133,13 @@ func PrintMessages(t *testing.T, c *gophercloud.ServiceClient, id string) error return nil } -func waitForStatus(t *testing.T, c *gophercloud.ServiceClient, id, status string) error { +func waitForStatus(t *testing.T, c *gophercloud.ServiceClient, id, status string) (*shares.Share, error) { + var current *shares.Share + err := tools.WaitFor(func() (bool, error) { - current, err := shares.Get(c, id).Extract() + var err error + + current, err = shares.Get(c, id).Extract() if err != nil { if _, ok := err.(gophercloud.ErrDefault404); ok { switch status { @@ -158,9 +166,9 @@ func waitForStatus(t *testing.T, c *gophercloud.ServiceClient, id, status string if err != nil { mErr := PrintMessages(t, c, id) if mErr != nil { - return fmt.Errorf("Share status is '%s' and unable to get manila messages: %s", err, mErr) + return current, fmt.Errorf("Share status is '%s' and unable to get manila messages: %s", err, mErr) } } - return err + return current, err } diff --git a/acceptance/openstack/sharedfilesystems/v2/shares_test.go b/acceptance/openstack/sharedfilesystems/v2/shares_test.go index 208e8993f6..b1c14d02b0 100644 --- a/acceptance/openstack/sharedfilesystems/v2/shares_test.go +++ b/acceptance/openstack/sharedfilesystems/v2/shares_test.go @@ -45,7 +45,7 @@ func TestShareExportLocations(t *testing.T) { defer DeleteShare(t, client, share) - err = waitForStatus(t, client, share.ID, "available") + _, err = waitForStatus(t, client, share.ID, "available") if err != nil { t.Fatalf("Share status error: %v", err) } @@ -219,7 +219,7 @@ func TestExtendAndShrink(t *testing.T) { } // We need to wait till the Extend operation is done - err = waitForStatus(t, client, share.ID, "available") + _, err = waitForStatus(t, client, share.ID, "available") if err != nil { t.Fatalf("Share status error: %v", err) } @@ -233,7 +233,7 @@ func TestExtendAndShrink(t *testing.T) { } // We need to wait till the Shrink operation is done - err = waitForStatus(t, client, share.ID, "available") + _, err = waitForStatus(t, client, share.ID, "available") if err != nil { t.Fatalf("Share status error: %v", err) } @@ -315,7 +315,7 @@ func TestRevert(t *testing.T) { defer DeleteShare(t, client, share) - err = waitForStatus(t, client, share.ID, "available") + _, err = waitForStatus(t, client, share.ID, "available") if err != nil { t.Fatalf("Share status error: %v", err) } @@ -340,7 +340,7 @@ func TestRevert(t *testing.T) { } // We need to wait till the Extend operation is done - err = waitForStatus(t, client, share.ID, "available") + _, err = waitForStatus(t, client, share.ID, "available") if err != nil { t.Fatalf("Share status error: %v", err) } @@ -353,6 +353,73 @@ func TestRevert(t *testing.T) { t.Logf("Share %s successfuly reverted", share.ID) } +func TestShareRestoreFromSnapshot(t *testing.T) { + client, err := clients.NewSharedFileSystemV2Client() + if err != nil { + t.Fatalf("Unable to create a shared file system client: %v", err) + } + client.Microversion = "2.27" + + shareType := "default" + share, err := CreateShare(t, client, shareType) + if err != nil { + t.Fatalf("Unable to create a share: %v", err) + } + + defer DeleteShare(t, client, share) + + _, err = waitForStatus(t, client, share.ID, "available") + if err != nil { + t.Fatalf("Share status error: %v", err) + } + + snapshot, err := CreateSnapshot(t, client, share.ID) + if err != nil { + t.Fatalf("Unable to create a snapshot: %v", err) + } + defer DeleteSnapshot(t, client, snapshot) + + err = waitForSnapshotStatus(t, client, snapshot.ID, "available") + if err != nil { + t.Fatalf("Snapshot status error: %v", err) + } + + // create a bigger share from a snapshot + iTrue := true + newSize := share.Size + 1 + createOpts := shares.CreateOpts{ + Size: newSize, + Name: "My Test Share", + Description: "My Test Description", + ShareProto: "NFS", + ShareType: shareType, + SnapshotID: snapshot.ID, + IsPublic: &iTrue, + } + restored, err := shares.Create(client, createOpts).Extract() + if err != nil { + t.Fatalf("Unable to create a share from a snapshot: %v", err) + } + defer DeleteShare(t, client, restored) + + if restored.Size != newSize { + t.Fatalf("Unexpected restored share size: %d", restored.Size) + } + + // We need to wait till the Extend operation is done + checkShare, err := waitForStatus(t, client, restored.ID, "available") + if err != nil { + t.Fatalf("Share status error: %v", err) + } + + t.Logf("Share %s has been successfully restored: %+#v", checkShare.ID, checkShare) + + err = waitForSnapshotStatus(t, client, snapshot.ID, "available") + if err != nil { + t.Fatalf("Snapshot status error: %v", err) + } +} + func TestResetStatus(t *testing.T) { client, err := clients.NewSharedFileSystemV2Client() if err != nil { @@ -367,7 +434,7 @@ func TestResetStatus(t *testing.T) { defer DeleteShare(t, client, share) - err = waitForStatus(t, client, share.ID, "available") + _, err = waitForStatus(t, client, share.ID, "available") if err != nil { t.Fatalf("Share status error: %v", err) } @@ -381,7 +448,7 @@ func TestResetStatus(t *testing.T) { } // We need to wait till the Extend operation is done - err = waitForStatus(t, client, share.ID, "error") + _, err = waitForStatus(t, client, share.ID, "error") if err != nil { t.Fatalf("Share status error: %v", err) } @@ -403,7 +470,7 @@ func TestForceDelete(t *testing.T) { defer DeleteShare(t, client, share) - err = waitForStatus(t, client, share.ID, "available") + _, err = waitForStatus(t, client, share.ID, "available") if err != nil { t.Fatalf("Share status error: %v", err) } @@ -413,7 +480,7 @@ func TestForceDelete(t *testing.T) { t.Fatalf("Unable to force delete a share: %v", err) } - err = waitForStatus(t, client, share.ID, "deleted") + _, err = waitForStatus(t, client, share.ID, "deleted") if err != nil { t.Fatalf("Share status error: %v", err) } @@ -437,7 +504,7 @@ func TestUnmanage(t *testing.T) { defer DeleteShare(t, client, share) - err = waitForStatus(t, client, share.ID, "available") + _, err = waitForStatus(t, client, share.ID, "available") if err != nil { t.Fatalf("Share status error: %v", err) } @@ -447,7 +514,7 @@ func TestUnmanage(t *testing.T) { t.Fatalf("Unable to unmanage a share: %v", err) } - err = waitForStatus(t, client, share.ID, "deleted") + _, err = waitForStatus(t, client, share.ID, "deleted") if err != nil { t.Fatalf("Share status error: %v", err) } From 4f66d1001cb7cd2104984ac269515bef5c9e5e0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= <m.andre@redhat.com> Date: Thu, 25 May 2023 10:02:10 +0200 Subject: [PATCH 249/360] Update changelog for new release --- CHANGELOG.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9e2567b98b..e737082d69 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,28 @@ +## 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 From 56edd8a770d27c42026e3a32080481a413753b4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= <m.andre@redhat.com> Date: Thu, 25 May 2023 10:03:06 +0200 Subject: [PATCH 250/360] Bump version --- provider_client.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/provider_client.go b/provider_client.go index c603d6dbe3..12273d8049 100644 --- a/provider_client.go +++ b/provider_client.go @@ -14,7 +14,7 @@ import ( // DefaultUserAgent is the default User-Agent string set in the request header. const ( - DefaultUserAgent = "gophercloud/v1.3.0" + DefaultUserAgent = "gophercloud/v1.4.0" DefaultMaxBackoffRetries = 60 ) From 8316e788798ed4ee5364645c2fcd9a893b623b86 Mon Sep 17 00:00:00 2001 From: Dmitry Tantsur <dtantsur@protonmail.com> Date: Thu, 25 May 2023 15:17:48 +0200 Subject: [PATCH 251/360] baremetal: update inspection inventory with recent additions Closes: #2632 --- .../v1/introspection/results.go | 36 ++++++++++++------- .../v1/introspection/testing/fixtures.go | 36 +++++++++++++++++-- 2 files changed, 57 insertions(+), 15 deletions(-) diff --git a/openstack/baremetalintrospection/v1/introspection/results.go b/openstack/baremetalintrospection/v1/introspection/results.go index 43cc53c299..34cea04621 100644 --- a/openstack/baremetalintrospection/v1/introspection/results.go +++ b/openstack/baremetalintrospection/v1/introspection/results.go @@ -176,6 +176,7 @@ type Data struct { RootDisk RootDiskType `json:"root_disk"` Extra ExtraHardwareDataType `json:"extra"` NUMATopology NUMATopology `json:"numa_topology"` + RawLLDP map[string][]LLDPTLVType `json:"lldp_raw"` } // Sub Types defined under Data and deeper in the structure @@ -207,16 +208,18 @@ type LLDPTLVType struct { } type InterfaceType struct { - BIOSDevName string `json:"biosdevname"` - ClientID string `json:"client_id"` - HasCarrier bool `json:"has_carrier"` - IPV4Address string `json:"ipv4_address"` - IPV6Address string `json:"ipv6_address"` - LLDP []LLDPTLVType `json:"lldp"` - MACAddress string `json:"mac_address"` - Name string `json:"name"` - Product string `json:"product"` - Vendor string `json:"vendor"` + BIOSDevName string `json:"biosdevname"` + ClientID string `json:"client_id"` + HasCarrier bool `json:"has_carrier"` + IPV4Address string `json:"ipv4_address"` + IPV6Address string `json:"ipv6_address"` + // Deprecated, see Data.RawLLDP + LLDP []LLDPTLVType `json:"lldp"` + MACAddress string `json:"mac_address"` + Name string `json:"name"` + Product string `json:"product"` + SpeedMbps int `json:"speed_mbps"` + Vendor string `json:"vendor"` } type InventoryType struct { @@ -249,10 +252,17 @@ type RootDiskType struct { WwnWithExtension string `json:"wwn_with_extension"` } +type SystemFirmwareType struct { + Version string `json:"version"` + BuildDate string `json:"build_data"` + Vendor string `json:"vendor"` +} + type SystemVendorType struct { - Manufacturer string `json:"manufacturer"` - ProductName string `json:"product_name"` - SerialNumber string `json:"serial_number"` + Manufacturer string `json:"manufacturer"` + ProductName string `json:"product_name"` + SerialNumber string `json:"serial_number"` + Firmware SystemFirmwareType `json:"firmware"` } type ExtraHardwareData map[string]interface{} diff --git a/openstack/baremetalintrospection/v1/introspection/testing/fixtures.go b/openstack/baremetalintrospection/v1/introspection/testing/fixtures.go index bc7bf3a24c..142b3a7469 100644 --- a/openstack/baremetalintrospection/v1/introspection/testing/fixtures.go +++ b/openstack/baremetalintrospection/v1/introspection/testing/fixtures.go @@ -160,7 +160,8 @@ const IntrospectionDataJSONSample = ` "mac_address": "52:54:00:4e:3d:30", "name": "eth0", "product": "0x0001", - "vendor": "0x1af4" + "vendor": "0x1af4", + "speed_mbps": 1000 } ], "memory": { @@ -170,10 +171,25 @@ const IntrospectionDataJSONSample = ` "system_vendor": { "manufacturer": "Bochs", "product_name": "Bochs", - "serial_number": "Not Specified" + "serial_number": "Not Specified", + "firmware": { + "version": "1.2.3.4" + } } }, "ipmi_address": "192.167.2.134", + "lldp_raw": { + "eth0": [ + [ + 1, + "04112233aabbcc" + ], + [ + 5, + "737730312d646973742d31622d623132" + ] + ] + }, "local_gb": 12, "macs": [ "52:54:00:4e:3d:30" @@ -377,6 +393,9 @@ var ( Manufacturer: "Bochs", ProductName: "Bochs", SerialNumber: "Not Specified", + Firmware: introspection.SystemFirmwareType{ + Version: "1.2.3.4", + }, }, BmcAddress: "192.167.2.134", Boot: introspection.BootInfoType{ @@ -425,6 +444,7 @@ var ( Value: "737730312d646973742d31622d623132", }, }, + SpeedMbps: 1000, }, }, Memory: introspection.MemoryType{ @@ -451,6 +471,18 @@ var ( }, }, }, + RawLLDP: map[string][]introspection.LLDPTLVType{ + "eth0": { + { + Type: 1, + Value: "04112233aabbcc", + }, + { + Type: 5, + Value: "737730312d646973742d31622d623132", + }, + }, + }, } IntrospectionExtraHardware = introspection.ExtraHardwareDataType{ From 2bee9bac945626037b2d6dfa87375b537fcbd2c4 Mon Sep 17 00:00:00 2001 From: Konstantin Eremin <kod-t@mail.ru> Date: Tue, 30 May 2023 14:22:26 +0300 Subject: [PATCH 252/360] Add FWaaS_v2 workflow. Add FWaaS_v2 workflow for master and Zed. Skip FWaaS_v2 tests for Openstack releases below Zed. Fix FWaaS_v2 test variable. --- .github/workflows/functional-fwaas_v2.yaml | 63 +++++++++++++++++++ .../v2/extensions/fwaas_v2/groups_test.go | 26 ++++---- .../v2/extensions/fwaas_v2/policy_test.go | 4 +- .../v2/extensions/fwaas_v2/rule_test.go | 4 +- 4 files changed, 83 insertions(+), 14 deletions(-) create mode 100644 .github/workflows/functional-fwaas_v2.yaml diff --git a/.github/workflows/functional-fwaas_v2.yaml b/.github/workflows/functional-fwaas_v2.yaml new file mode 100644 index 0000000000..d13104c6a9 --- /dev/null +++ b/.github/workflows/functional-fwaas_v2.yaml @@ -0,0 +1,63 @@ +name: functional-networking +on: + pull_request: + paths: + - '**networking**' + schedule: + - cron: '0 0 */3 * *' +jobs: + functional-networking: + strategy: + fail-fast: false + matrix: + include: + - name: "master" + openstack_version: "master" + ubuntu_version: "22.04" + devstack_conf_overrides: | + enable_plugin neutron-fwaas https://opendev.org/openstack/neutron-fwaas master + enable_plugin neutron-fwaas-dashboard https://opendev.org/openstack/neutron-fwaas-dashboard master + - name: "zed" + openstack_version: "stable/zed" + ubuntu_version: "22.04" + devstack_conf_overrides: | + enable_plugin neutron-fwaas https://opendev.org/openstack/neutron-fwaas stable/zed + enable_plugin neutron-fwaas-dashboard https://opendev.org/openstack/neutron-fwaas-dashboard stable/zed + runs-on: ubuntu-${{ matrix.ubuntu_version }} + name: Deploy OpenStack ${{ matrix.name }} with enabled FWaaS_v2 and run networking acceptance tests + steps: + - name: Checkout Gophercloud + uses: actions/checkout@v3 + - name: Deploy devstack + uses: EmilienM/devstack-action@v0.11 + with: + branch: ${{ matrix.openstack_version }} + conf_overrides: | + Q_AGENT=openvswitch + Q_ML2_PLUGIN_MECHANISM_DRIVERS=openvswitch,l2population + Q_ML2_PLUGIN_TYPE_DRIVERS=flat,gre,vlan,vxlan + Q_ML2_TENANT_NETWORK_TYPE=vxlan + Q_TUNNEL_TYPES=vxlan,gre + enable_plugin neutron-fwaas https://opendev.org/openstack/neutron-fwaas stable/zed + enable_plugin neutron-fwaas-dashboard https://opendev.org/openstack/neutron-fwaas-dashboard stable/zed + enabled_services: 'q-svc,q-agt,q-dhcp,q-l3,q-meta,q-fwaas-v2' + disabled_services: 'ovn,ovn-controller,ovn-northd,q-ovn-metadata-agent,cinder,c-sch,c-api,c-vol,horizon,tempest,swift' + - name: Checkout go + uses: actions/setup-go@v4 + with: + go-version: '^1.15' + - name: Run Gophercloud acceptance tests + run: ./script/acceptancetest + env: + DEVSTACK_PATH: ${{ github.workspace }}/devstack + ACCEPTANCE_TESTS_FILTER: "^.*fwaas_v2.*$" + OS_BRANCH: ${{ matrix.openstack_version }} + - name: Generate logs on failure + run: ./script/collectlogs + if: failure() + - name: Upload logs artifacts on failure + if: failure() + uses: actions/upload-artifact@v3 + with: + name: functional-networking-${{ matrix.name }} + path: /tmp/devstack-logs/* diff --git a/acceptance/openstack/networking/v2/extensions/fwaas_v2/groups_test.go b/acceptance/openstack/networking/v2/extensions/fwaas_v2/groups_test.go index 1123e3c217..0183f3c8ab 100644 --- a/acceptance/openstack/networking/v2/extensions/fwaas_v2/groups_test.go +++ b/acceptance/openstack/networking/v2/extensions/fwaas_v2/groups_test.go @@ -13,7 +13,9 @@ import ( ) func TestGroupCRUD(t *testing.T) { - clients.SkipReleasesAbove(t, "stable/ussuri") + // Releases below Victoria are not maintained. + // FWaaS_v2 is not compatible with releases below Zed. + clients.SkipReleasesBelow(t, "stable/zed") client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) @@ -48,21 +50,21 @@ func TestGroupCRUD(t *testing.T) { EgressFirewallPolicyID: &firewall_policy_id, } - groupUpdated, err := groups.Update(client, createdGroup.ID, updateOpts).Extract() + updatedGroup, err := groups.Update(client, createdGroup.ID, updateOpts).Extract() if err != nil { t.Fatalf("Unable to update firewall group %s: %v", createdGroup.ID, err) } th.AssertNoErr(t, err) - th.AssertEquals(t, groupUpdated.Name, groupName) - th.AssertEquals(t, groupUpdated.Description, description) - th.AssertEquals(t, groupUpdated.AdminStateUp, adminStateUp) - th.AssertEquals(t, groupUpdated.IngressFirewallPolicyID, firewall_policy_id) - th.AssertEquals(t, groupUpdated.EgressFirewallPolicyID, firewall_policy_id) + th.AssertEquals(t, updatedGroup.Name, groupName) + th.AssertEquals(t, updatedGroup.Description, description) + th.AssertEquals(t, updatedGroup.AdminStateUp, adminStateUp) + th.AssertEquals(t, updatedGroup.IngressFirewallPolicyID, firewall_policy_id) + th.AssertEquals(t, updatedGroup.EgressFirewallPolicyID, firewall_policy_id) - t.Logf("Updated firewall group %s", groupUpdated.ID) + t.Logf("Updated firewall group %s", updatedGroup.ID) - removeIngressPolicy, err := groups.RemoveIngressPolicy(client, groupUpdated.ID).Extract() + removeIngressPolicy, err := groups.RemoveIngressPolicy(client, updatedGroup.ID).Extract() if err != nil { t.Fatalf("Unable to remove ingress firewall policy from firewall group %s: %v", removeIngressPolicy.ID, err) } @@ -70,16 +72,16 @@ func TestGroupCRUD(t *testing.T) { th.AssertEquals(t, removeIngressPolicy.IngressFirewallPolicyID, "") th.AssertEquals(t, removeIngressPolicy.EgressFirewallPolicyID, firewall_policy_id) - t.Logf("Ingress policy removed from firewall group %s", groupUpdated.ID) + t.Logf("Ingress policy removed from firewall group %s", updatedGroup.ID) - removeEgressPolicy, err := groups.RemoveEgressPolicy(client, groupUpdated.ID).Extract() + removeEgressPolicy, err := groups.RemoveEgressPolicy(client, updatedGroup.ID).Extract() if err != nil { t.Fatalf("Unable to remove egress firewall policy from firewall group %s: %v", removeEgressPolicy.ID, err) } th.AssertEquals(t, removeEgressPolicy.EgressFirewallPolicyID, "") - t.Logf("Egress policy removed from firewall group %s", groupUpdated.ID) + t.Logf("Egress policy removed from firewall group %s", updatedGroup.ID) allPages, err := groups.List(client, nil).AllPages() th.AssertNoErr(t, err) diff --git a/acceptance/openstack/networking/v2/extensions/fwaas_v2/policy_test.go b/acceptance/openstack/networking/v2/extensions/fwaas_v2/policy_test.go index 45a1c9860f..b2cc55ae64 100644 --- a/acceptance/openstack/networking/v2/extensions/fwaas_v2/policy_test.go +++ b/acceptance/openstack/networking/v2/extensions/fwaas_v2/policy_test.go @@ -13,7 +13,9 @@ import ( ) func TestPolicyCRUD(t *testing.T) { - clients.SkipReleasesAbove(t, "stable/ussuri") + // Releases below Victoria are not maintained. + // FWaaS_v2 is not compatible with releases below Zed. + clients.SkipReleasesBelow(t, "stable/zed") client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) diff --git a/acceptance/openstack/networking/v2/extensions/fwaas_v2/rule_test.go b/acceptance/openstack/networking/v2/extensions/fwaas_v2/rule_test.go index 36c9ea720c..43549fd622 100644 --- a/acceptance/openstack/networking/v2/extensions/fwaas_v2/rule_test.go +++ b/acceptance/openstack/networking/v2/extensions/fwaas_v2/rule_test.go @@ -15,7 +15,9 @@ import ( ) func TestRuleCRUD(t *testing.T) { - clients.SkipReleasesAbove(t, "stable/ussuri") + // Releases below Victoria are not maintained. + // FWaaS_v2 is not compatible with releases below Zed. + clients.SkipReleasesBelow(t, "stable/zed") client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) From 42579311a2fedc171deb3b610526ba46b887d206 Mon Sep 17 00:00:00 2001 From: Konstantin Eremin <kod-t@mail.ru> Date: Tue, 30 May 2023 14:43:52 +0300 Subject: [PATCH 253/360] Remove unused overrides --- .github/workflows/functional-fwaas_v2.yaml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/functional-fwaas_v2.yaml b/.github/workflows/functional-fwaas_v2.yaml index d13104c6a9..5035bbcaf9 100644 --- a/.github/workflows/functional-fwaas_v2.yaml +++ b/.github/workflows/functional-fwaas_v2.yaml @@ -38,8 +38,6 @@ jobs: Q_ML2_PLUGIN_TYPE_DRIVERS=flat,gre,vlan,vxlan Q_ML2_TENANT_NETWORK_TYPE=vxlan Q_TUNNEL_TYPES=vxlan,gre - enable_plugin neutron-fwaas https://opendev.org/openstack/neutron-fwaas stable/zed - enable_plugin neutron-fwaas-dashboard https://opendev.org/openstack/neutron-fwaas-dashboard stable/zed enabled_services: 'q-svc,q-agt,q-dhcp,q-l3,q-meta,q-fwaas-v2' disabled_services: 'ovn,ovn-controller,ovn-northd,q-ovn-metadata-agent,cinder,c-sch,c-api,c-vol,horizon,tempest,swift' - name: Checkout go From 4019c7670d0cb8a12358a26994ca868573d66da4 Mon Sep 17 00:00:00 2001 From: Konstantin Eremin <kod-t@mail.ru> Date: Tue, 30 May 2023 14:54:51 +0300 Subject: [PATCH 254/360] Rename networking to fwaas_v2 --- .github/workflows/functional-fwaas_v2.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/functional-fwaas_v2.yaml b/.github/workflows/functional-fwaas_v2.yaml index 5035bbcaf9..8325d4e0d4 100644 --- a/.github/workflows/functional-fwaas_v2.yaml +++ b/.github/workflows/functional-fwaas_v2.yaml @@ -1,12 +1,12 @@ -name: functional-networking +name: functional-fwaas_v2 on: pull_request: paths: - - '**networking**' + - '**networking/extensions/fwaas_v2**' schedule: - cron: '0 0 */3 * *' jobs: - functional-networking: + functional-fwaas_v2: strategy: fail-fast: false matrix: @@ -57,5 +57,5 @@ jobs: if: failure() uses: actions/upload-artifact@v3 with: - name: functional-networking-${{ matrix.name }} + name: functional-fwaas_v2-${{ matrix.name }} path: /tmp/devstack-logs/* From 1df5e7dbc6309afb91884a6bcf88e85cff7d40ed Mon Sep 17 00:00:00 2001 From: Konstantin Eremin <kod-t@mail.ru> Date: Tue, 30 May 2023 16:47:37 +0300 Subject: [PATCH 255/360] Exclude fwaas_v2 from networking workflow acceptance tests filter --- .github/workflows/functional-networking.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/functional-networking.yaml b/.github/workflows/functional-networking.yaml index 461acdff16..3f69e5abbe 100644 --- a/.github/workflows/functional-networking.yaml +++ b/.github/workflows/functional-networking.yaml @@ -68,7 +68,7 @@ jobs: run: ./script/acceptancetest env: DEVSTACK_PATH: ${{ github.workspace }}/devstack - ACCEPTANCE_TESTS_FILTER: "^.*networking.*$" + ACCEPTANCE_TESTS_FILTER: "^(?!.*fwaas_v2.*).*networking.*$" OS_BRANCH: ${{ matrix.openstack_version }} - name: Generate logs on failure run: ./script/collectlogs From 86ed77050d93374260d382f72d90e2151032dbb2 Mon Sep 17 00:00:00 2001 From: Pierre Prinetti <pierreprinetti@redhat.com> Date: Fri, 2 Jun 2023 14:37:27 +0200 Subject: [PATCH 256/360] Implement errors.Unwrap() on unexpected status code errors With this patch, we add a method with signature `Unwrap() error` to all errors signaling an unexpected status code. All of them contain a value of type `gophercloud.ErrUnexpectedStatusCode` that exposes, among other useful values, the body of the response sent by the server. To access the response body of a request that resulted in an unexpected response code, try to unwrap the returned error to `gophercloud.ErrUnexpectedStatusCode`: ```Go pages, err := containers.List(client, nil).AllPages() if err != nil { var responseCodeError gophercloud.ErrUnexpectedResponseCode if errors.As(err, &responseCodeError) { log.Printf("unexpected response code. Response body:\n---\n%s\n---", responseCodeError.Body) } else { log.Printf("unknown error") } } ``` --- errors.go | 48 ++++++++++++++++++++++++++++++++++++ testing/errors_test.go | 55 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 102 insertions(+), 1 deletion(-) diff --git a/errors.go b/errors.go index edba02badf..8ab592ca49 100644 --- a/errors.go +++ b/errors.go @@ -116,61 +116,109 @@ type ErrDefault400 struct { ErrUnexpectedResponseCode } +func (e ErrDefault400) Unwrap() error { + return e.ErrUnexpectedResponseCode +} + // ErrDefault401 is the default error type returned on a 401 HTTP response code. type ErrDefault401 struct { ErrUnexpectedResponseCode } +func (e ErrDefault401) Unwrap() error { + return e.ErrUnexpectedResponseCode +} + // ErrDefault403 is the default error type returned on a 403 HTTP response code. type ErrDefault403 struct { ErrUnexpectedResponseCode } +func (e ErrDefault403) Unwrap() error { + return e.ErrUnexpectedResponseCode +} + // ErrDefault404 is the default error type returned on a 404 HTTP response code. type ErrDefault404 struct { ErrUnexpectedResponseCode } +func (e ErrDefault404) Unwrap() error { + return e.ErrUnexpectedResponseCode +} + // ErrDefault405 is the default error type returned on a 405 HTTP response code. type ErrDefault405 struct { ErrUnexpectedResponseCode } +func (e ErrDefault405) Unwrap() error { + return e.ErrUnexpectedResponseCode +} + // ErrDefault408 is the default error type returned on a 408 HTTP response code. type ErrDefault408 struct { ErrUnexpectedResponseCode } +func (e ErrDefault408) Unwrap() error { + return e.ErrUnexpectedResponseCode +} + // ErrDefault409 is the default error type returned on a 409 HTTP response code. type ErrDefault409 struct { ErrUnexpectedResponseCode } +func (e ErrDefault409) Unwrap() error { + return e.ErrUnexpectedResponseCode +} + // ErrDefault429 is the default error type returned on a 429 HTTP response code. type ErrDefault429 struct { ErrUnexpectedResponseCode } +func (e ErrDefault429) Unwrap() error { + return e.ErrUnexpectedResponseCode +} + // ErrDefault500 is the default error type returned on a 500 HTTP response code. type ErrDefault500 struct { ErrUnexpectedResponseCode } +func (e ErrDefault500) Unwrap() error { + return e.ErrUnexpectedResponseCode +} + // ErrDefault502 is the default error type returned on a 502 HTTP response code. type ErrDefault502 struct { ErrUnexpectedResponseCode } +func (e ErrDefault502) Unwrap() error { + return e.ErrUnexpectedResponseCode +} + // ErrDefault503 is the default error type returned on a 503 HTTP response code. type ErrDefault503 struct { ErrUnexpectedResponseCode } +func (e ErrDefault503) Unwrap() error { + return e.ErrUnexpectedResponseCode +} + // ErrDefault504 is the default error type returned on a 504 HTTP response code. type ErrDefault504 struct { ErrUnexpectedResponseCode } +func (e ErrDefault504) Unwrap() error { + return e.ErrUnexpectedResponseCode +} + func (e ErrDefault400) Error() string { e.DefaultErrString = fmt.Sprintf( "Bad request with: [%s %s], error message: %s", diff --git a/testing/errors_test.go b/testing/errors_test.go index eb379a49d9..2663c6bc77 100644 --- a/testing/errors_test.go +++ b/testing/errors_test.go @@ -1,6 +1,7 @@ package testing import ( + "errors" "testing" "github.com/gophercloud/gophercloud" @@ -13,7 +14,7 @@ func returnsUnexpectedResp(code int) gophercloud.ErrUnexpectedResponseCode { Method: "GET", Expected: []int{200}, Actual: code, - Body: nil, + Body: []byte("the response body"), ResponseHeader: nil, } } @@ -24,6 +25,17 @@ func TestGetResponseCode404(t *testing.T) { err, ok := err404.(gophercloud.StatusCodeError) th.AssertEquals(t, true, ok) th.AssertEquals(t, err.GetStatusCode(), 404) + + t.Run("wraps ErrUnexpectedResponseCode", func(t *testing.T) { + var unexpectedResponseCode gophercloud.ErrUnexpectedResponseCode + if errors.As(err, &unexpectedResponseCode) { + if want, have := "the response body", string(unexpectedResponseCode.Body); want != have { + t.Errorf("expected the wrapped error to contain the response body, found %q", have) + } + } else { + t.Errorf("err.Unwrap() didn't return ErrUnexpectedResponseCode") + } + }) } func TestGetResponseCode502(t *testing.T) { @@ -32,6 +44,17 @@ func TestGetResponseCode502(t *testing.T) { err, ok := err502.(gophercloud.StatusCodeError) th.AssertEquals(t, true, ok) th.AssertEquals(t, err.GetStatusCode(), 502) + + t.Run("wraps ErrUnexpectedResponseCode", func(t *testing.T) { + var unexpectedResponseCode gophercloud.ErrUnexpectedResponseCode + if errors.As(err, &unexpectedResponseCode) { + if want, have := "the response body", string(unexpectedResponseCode.Body); want != have { + t.Errorf("expected the wrapped error to contain the response body, found %q", have) + } + } else { + t.Errorf("err.Unwrap() didn't return ErrUnexpectedResponseCode") + } + }) } func TestGetResponseCode504(t *testing.T) { @@ -40,4 +63,34 @@ func TestGetResponseCode504(t *testing.T) { err, ok := err504.(gophercloud.StatusCodeError) th.AssertEquals(t, true, ok) th.AssertEquals(t, err.GetStatusCode(), 504) + + t.Run("wraps ErrUnexpectedResponseCode", func(t *testing.T) { + var unexpectedResponseCode gophercloud.ErrUnexpectedResponseCode + if errors.As(err, &unexpectedResponseCode) { + if want, have := "the response body", string(unexpectedResponseCode.Body); want != have { + t.Errorf("expected the wrapped error to contain the response body, found %q", have) + } + } else { + t.Errorf("err.Unwrap() didn't return ErrUnexpectedResponseCode") + } + }) } + +// Compile-time check that all response-code errors implement `Unwrap()` +type unwrapper interface { + Unwrap() error +} + +var ( + _ unwrapper = gophercloud.ErrDefault401{} + _ unwrapper = gophercloud.ErrDefault403{} + _ unwrapper = gophercloud.ErrDefault404{} + _ unwrapper = gophercloud.ErrDefault405{} + _ unwrapper = gophercloud.ErrDefault408{} + _ unwrapper = gophercloud.ErrDefault409{} + _ unwrapper = gophercloud.ErrDefault429{} + _ unwrapper = gophercloud.ErrDefault500{} + _ unwrapper = gophercloud.ErrDefault502{} + _ unwrapper = gophercloud.ErrDefault503{} + _ unwrapper = gophercloud.ErrDefault504{} +) From b5d1d3abb33bec1056ab33bbb8d45c5e563251c1 Mon Sep 17 00:00:00 2001 From: Dmitry Tantsur <dtantsur@protonmail.com> Date: Tue, 6 Jun 2023 13:51:21 +0200 Subject: [PATCH 257/360] baremetal: fix a typo in SystemFirmwareType MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Martin André <martin.andre@gmail.com> --- openstack/baremetalintrospection/v1/introspection/results.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openstack/baremetalintrospection/v1/introspection/results.go b/openstack/baremetalintrospection/v1/introspection/results.go index 34cea04621..2c197fa07f 100644 --- a/openstack/baremetalintrospection/v1/introspection/results.go +++ b/openstack/baremetalintrospection/v1/introspection/results.go @@ -254,7 +254,7 @@ type RootDiskType struct { type SystemFirmwareType struct { Version string `json:"version"` - BuildDate string `json:"build_data"` + BuildDate string `json:"build_date"` Vendor string `json:"vendor"` } From 832311259dc190d56bc0a03f86e0e1869970c57e Mon Sep 17 00:00:00 2001 From: Konstantin Eremin <kod-t@mail.ru> Date: Wed, 7 Jun 2023 18:48:02 +0300 Subject: [PATCH 258/360] Disabled devstack services move to enabled_services --- .github/workflows/functional-fwaas_v2.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/functional-fwaas_v2.yaml b/.github/workflows/functional-fwaas_v2.yaml index 8325d4e0d4..0b7f40765a 100644 --- a/.github/workflows/functional-fwaas_v2.yaml +++ b/.github/workflows/functional-fwaas_v2.yaml @@ -38,8 +38,7 @@ jobs: Q_ML2_PLUGIN_TYPE_DRIVERS=flat,gre,vlan,vxlan Q_ML2_TENANT_NETWORK_TYPE=vxlan Q_TUNNEL_TYPES=vxlan,gre - enabled_services: 'q-svc,q-agt,q-dhcp,q-l3,q-meta,q-fwaas-v2' - disabled_services: 'ovn,ovn-controller,ovn-northd,q-ovn-metadata-agent,cinder,c-sch,c-api,c-vol,horizon,tempest,swift' + enabled_services: 'q-svc,q-agt,q-dhcp,q-l3,q-meta,q-fwaas-v2,-cinder,-horizon,-tempest,-swift,-c-sch,-c-api,-c-vol,-c-bak,-ovn,-ovn-controller,-ovn-northd,-q-ovn-metadata-agent' - name: Checkout go uses: actions/setup-go@v4 with: From eea885cca2114ea9a4d00ad5107277367e29bf25 Mon Sep 17 00:00:00 2001 From: Konstantin Eremin <kod-t@mail.ru> Date: Thu, 8 Jun 2023 11:47:50 +0300 Subject: [PATCH 259/360] Workflow: add antelope, enabled_plugins move to conf_overrides --- .github/workflows/functional-fwaas_v2.yaml | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/.github/workflows/functional-fwaas_v2.yaml b/.github/workflows/functional-fwaas_v2.yaml index 0b7f40765a..08b9c13ba0 100644 --- a/.github/workflows/functional-fwaas_v2.yaml +++ b/.github/workflows/functional-fwaas_v2.yaml @@ -10,19 +10,16 @@ jobs: strategy: fail-fast: false matrix: + name: ["master"] + openstack_version: ["master"] + ubuntu_version: ["22.04"] include: - - name: "master" - openstack_version: "master" + - name: "antelope" + openstack_version: "stable/2023.1" ubuntu_version: "22.04" - devstack_conf_overrides: | - enable_plugin neutron-fwaas https://opendev.org/openstack/neutron-fwaas master - enable_plugin neutron-fwaas-dashboard https://opendev.org/openstack/neutron-fwaas-dashboard master - name: "zed" openstack_version: "stable/zed" ubuntu_version: "22.04" - devstack_conf_overrides: | - enable_plugin neutron-fwaas https://opendev.org/openstack/neutron-fwaas stable/zed - enable_plugin neutron-fwaas-dashboard https://opendev.org/openstack/neutron-fwaas-dashboard stable/zed runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Deploy OpenStack ${{ matrix.name }} with enabled FWaaS_v2 and run networking acceptance tests steps: @@ -33,6 +30,8 @@ jobs: with: branch: ${{ matrix.openstack_version }} conf_overrides: | + enable_plugin neutron-fwaas https://opendev.org/openstack/neutron-fwaas ${{ matrix.openstack_version }} + enable_plugin neutron-fwaas-dashboard https://opendev.org/openstack/neutron-fwaas-dashboard ${{ matrix.openstack_version }} Q_AGENT=openvswitch Q_ML2_PLUGIN_MECHANISM_DRIVERS=openvswitch,l2population Q_ML2_PLUGIN_TYPE_DRIVERS=flat,gre,vlan,vxlan From 0140149162f8e233a723eb08f8b80c03bd4461c9 Mon Sep 17 00:00:00 2001 From: Konstantin Eremin <kod-t@mail.ru> Date: Thu, 8 Jun 2023 14:48:37 +0300 Subject: [PATCH 260/360] Workflow: disable neutron-fwaas-dashboard --- .github/workflows/functional-fwaas_v2.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/functional-fwaas_v2.yaml b/.github/workflows/functional-fwaas_v2.yaml index 08b9c13ba0..4c0b4463ee 100644 --- a/.github/workflows/functional-fwaas_v2.yaml +++ b/.github/workflows/functional-fwaas_v2.yaml @@ -31,7 +31,6 @@ jobs: branch: ${{ matrix.openstack_version }} conf_overrides: | enable_plugin neutron-fwaas https://opendev.org/openstack/neutron-fwaas ${{ matrix.openstack_version }} - enable_plugin neutron-fwaas-dashboard https://opendev.org/openstack/neutron-fwaas-dashboard ${{ matrix.openstack_version }} Q_AGENT=openvswitch Q_ML2_PLUGIN_MECHANISM_DRIVERS=openvswitch,l2population Q_ML2_PLUGIN_TYPE_DRIVERS=flat,gre,vlan,vxlan From 16409dc6c2f89bbed5c755f7b0669659c2854afb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 9 Jun 2023 10:00:45 +0000 Subject: [PATCH 261/360] Bump mheap/github-action-required-labels from 4 to 5 Bumps [mheap/github-action-required-labels](https://github.com/mheap/github-action-required-labels) from 4 to 5. - [Release notes](https://github.com/mheap/github-action-required-labels/releases) - [Commits](https://github.com/mheap/github-action-required-labels/compare/v4...v5) --- updated-dependencies: - dependency-name: mheap/github-action-required-labels dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> --- .github/workflows/semver-require.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/semver-require.yaml b/.github/workflows/semver-require.yaml index 99083ecce1..d584899306 100644 --- a/.github/workflows/semver-require.yaml +++ b/.github/workflows/semver-require.yaml @@ -10,7 +10,7 @@ jobs: semver: runs-on: ubuntu-latest steps: - - uses: mheap/github-action-required-labels@v4 + - uses: mheap/github-action-required-labels@v5 with: mode: exactly count: 1 From 14d31d3ad33f95cd2f6ad3ab9d48b411f95e2a59 Mon Sep 17 00:00:00 2001 From: kayrus <kay.diam@gmail.com> Date: Fri, 26 May 2023 11:51:23 +0200 Subject: [PATCH 262/360] [manila]: Add Share Replicas support --- acceptance/clients/conditions.go | 8 + .../sharedfilesystems/v2/replicas.go | 142 +++++++ .../sharedfilesystems/v2/replicas_test.go | 308 +++++++++++++++ .../v2/schedulerstats_test.go | 2 +- .../sharedfilesystems/v2/replicas/requests.go | 271 +++++++++++++ .../sharedfilesystems/v2/replicas/results.go | 272 +++++++++++++ .../v2/replicas/testing/fixtures.go | 356 ++++++++++++++++++ .../v2/replicas/testing/request_test.go | 269 +++++++++++++ .../sharedfilesystems/v2/replicas/urls.go | 35 ++ 9 files changed, 1662 insertions(+), 1 deletion(-) create mode 100644 acceptance/openstack/sharedfilesystems/v2/replicas.go create mode 100644 acceptance/openstack/sharedfilesystems/v2/replicas_test.go create mode 100644 openstack/sharedfilesystems/v2/replicas/requests.go create mode 100644 openstack/sharedfilesystems/v2/replicas/results.go create mode 100644 openstack/sharedfilesystems/v2/replicas/testing/fixtures.go create mode 100644 openstack/sharedfilesystems/v2/replicas/testing/request_test.go create mode 100644 openstack/sharedfilesystems/v2/replicas/urls.go diff --git a/acceptance/clients/conditions.go b/acceptance/clients/conditions.go index d6ef342e68..ab33cb972c 100644 --- a/acceptance/clients/conditions.go +++ b/acceptance/clients/conditions.go @@ -12,6 +12,14 @@ func RequiredSystemScope(t *testing.T) { } } +// RequireManilaReplicas will restrict a test to only be run with enabled +// manila replicas. +func RequireManilaReplicas(t *testing.T) { + if os.Getenv("OS_MANILA_REPLICAS") != "true" { + t.Skip("manila replicas must be enabled to run this test") + } +} + // RequireAdmin will restrict a test to only be run by admin users. func RequireAdmin(t *testing.T) { if os.Getenv("OS_USERNAME") != "admin" { diff --git a/acceptance/openstack/sharedfilesystems/v2/replicas.go b/acceptance/openstack/sharedfilesystems/v2/replicas.go new file mode 100644 index 0000000000..5756986c78 --- /dev/null +++ b/acceptance/openstack/sharedfilesystems/v2/replicas.go @@ -0,0 +1,142 @@ +package v2 + +import ( + "fmt" + "strings" + "testing" + + "github.com/gophercloud/gophercloud/acceptance/tools" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/replicas" + "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/shares" +) + +// CreateReplica will create a replica from shareID. An error will be returned +// if the replica could not be created. +func CreateReplica(t *testing.T, client *gophercloud.ServiceClient, share *shares.Share) (*replicas.Replica, error) { + createOpts := replicas.CreateOpts{ + ShareID: share.ID, + AvailabilityZone: share.AvailabilityZone, + } + + replica, err := replicas.Create(client, createOpts).Extract() + if err != nil { + t.Logf("Failed to create replica") + return nil, err + } + + _, err = waitForReplicaStatus(t, client, replica.ID, "available") + if err != nil { + t.Logf("Failed to get %s replica status", replica.ID) + DeleteReplica(t, client, replica) + return replica, err + } + + return replica, nil +} + +// DeleteReplica will delete a replica. A fatal error will occur if the replica +// failed to be deleted. This works best when used as a deferred function. +func DeleteReplica(t *testing.T, client *gophercloud.ServiceClient, replica *replicas.Replica) { + err := replicas.Delete(client, replica.ID).ExtractErr() + if err != nil { + if _, ok := err.(gophercloud.ErrDefault404); ok { + return + } + t.Errorf("Unable to delete replica %s: %v", replica.ID, err) + } + + _, err = waitForReplicaStatus(t, client, replica.ID, "deleted") + if err != nil { + t.Errorf("Failed to wait for 'deleted' status for %s replica: %v", replica.ID, err) + } else { + t.Logf("Deleted replica: %s", replica.ID) + } +} + +// ListShareReplicas lists all replicas that belong to shareID. +// An error will be returned if the replicas could not be listed.. +func ListShareReplicas(t *testing.T, client *gophercloud.ServiceClient, shareID string) ([]replicas.Replica, error) { + opts := replicas.ListOpts{ + ShareID: shareID, + } + pages, err := replicas.List(client, opts).AllPages() + if err != nil { + t.Errorf("Unable to list %q share replicas: %v", shareID, err) + } + + return replicas.ExtractReplicas(pages) +} + +func waitForReplicaStatus(t *testing.T, c *gophercloud.ServiceClient, id, status string) (*replicas.Replica, error) { + var current *replicas.Replica + + err := tools.WaitFor(func() (bool, error) { + var err error + + current, err = replicas.Get(c, id).Extract() + if err != nil { + if _, ok := err.(gophercloud.ErrDefault404); ok { + switch status { + case "deleted": + return true, nil + default: + return false, err + } + } + return false, err + } + + if current.Status == status { + return true, nil + } + + if strings.Contains(current.Status, "error") { + return true, fmt.Errorf("An error occurred, wrong status: %s", current.Status) + } + + return false, nil + }) + + if err != nil { + mErr := PrintMessages(t, c, id) + if mErr != nil { + return current, fmt.Errorf("Replica status is '%s' and unable to get manila messages: %s", err, mErr) + } + } + + return current, err +} + +func waitForReplicaState(t *testing.T, c *gophercloud.ServiceClient, id, state string) (*replicas.Replica, error) { + var current *replicas.Replica + + err := tools.WaitFor(func() (bool, error) { + var err error + + current, err = replicas.Get(c, id).Extract() + if err != nil { + return false, err + } + + if current.State == state { + return true, nil + } + + if strings.Contains(current.State, "error") { + return true, fmt.Errorf("An error occurred, wrong state: %s", current.State) + } + + return false, nil + }) + + if err != nil { + mErr := PrintMessages(t, c, id) + if mErr != nil { + return current, fmt.Errorf("Replica state is '%s' and unable to get manila messages: %s", err, mErr) + } + } + + return current, err +} diff --git a/acceptance/openstack/sharedfilesystems/v2/replicas_test.go b/acceptance/openstack/sharedfilesystems/v2/replicas_test.go new file mode 100644 index 0000000000..d3179b199f --- /dev/null +++ b/acceptance/openstack/sharedfilesystems/v2/replicas_test.go @@ -0,0 +1,308 @@ +//go:build acceptance +// +build acceptance + +package v2 + +import ( + "testing" + "time" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/replicas" + th "github.com/gophercloud/gophercloud/testhelper" +) + +// otherwise we need to set "X-OpenStack-Manila-API-Experimental: true" +const replicasMicroversion = "2.60" + +func TestReplicaCreate(t *testing.T) { + clients.RequireManilaReplicas(t) + + client, err := clients.NewSharedFileSystemV2Client() + if err != nil { + t.Fatalf("Unable to create a shared file system client: %v", err) + } + client.Microversion = replicasMicroversion + + share, err := CreateShare(t, client) + if err != nil { + t.Fatalf("Unable to create a share: %v", err) + } + + defer DeleteShare(t, client, share) + + replica, err := CreateReplica(t, client, share) + if err != nil { + t.Fatalf("Unable to create a replica: %v", err) + } + + defer DeleteReplica(t, client, replica) + + created, err := replicas.Get(client, replica.ID).Extract() + if err != nil { + t.Errorf("Unable to retrieve replica: %v", err) + } + tools.PrintResource(t, created) + + allReplicas, err := ListShareReplicas(t, client, share.ID) + th.AssertNoErr(t, err) + + if len(allReplicas) != 2 { + t.Errorf("Unable to list all two replicas") + } +} + +func TestReplicaPromote(t *testing.T) { + clients.RequireManilaReplicas(t) + + client, err := clients.NewSharedFileSystemV2Client() + if err != nil { + t.Fatalf("Unable to create a shared file system client: %v", err) + } + client.Microversion = replicasMicroversion + + share, err := CreateShare(t, client) + if err != nil { + t.Fatalf("Unable to create a share: %v", err) + } + + defer DeleteShare(t, client, share) + + replica, err := CreateReplica(t, client, share) + if err != nil { + t.Fatalf("Unable to create a replica: %v", err) + } + + defer DeleteReplica(t, client, replica) + + created, err := replicas.Get(client, replica.ID).Extract() + if err != nil { + t.Fatalf("Unable to retrieve replica: %v", err) + } + tools.PrintResource(t, created) + + // sync new replica + err = replicas.Resync(client, created.ID).ExtractErr() + th.AssertNoErr(t, err) + _, err = waitForReplicaState(t, client, created.ID, "in_sync") + if err != nil { + t.Fatalf("Replica status error: %v", err) + } + + // promote new replica + err = replicas.Promote(client, created.ID, &replicas.PromoteOpts{}).ExtractErr() + th.AssertNoErr(t, err) + + _, err = waitForReplicaState(t, client, created.ID, "active") + if err != nil { + t.Fatalf("Replica status error: %v", err) + } + + // promote old replica + allReplicas, err := ListShareReplicas(t, client, share.ID) + th.AssertNoErr(t, err) + var oldReplicaID string + for _, v := range allReplicas { + if v.ID == created.ID { + // These are not the droids you are looking for + continue + } + oldReplicaID = v.ID + } + if oldReplicaID == "" { + t.Errorf("Unable to get old replica") + } + // sync old replica + err = replicas.Resync(client, oldReplicaID).ExtractErr() + th.AssertNoErr(t, err) + _, err = waitForReplicaState(t, client, oldReplicaID, "in_sync") + if err != nil { + t.Fatalf("Replica status error: %v", err) + } + err = replicas.Promote(client, oldReplicaID, &replicas.PromoteOpts{}).ExtractErr() + th.AssertNoErr(t, err) + + _, err = waitForReplicaState(t, client, oldReplicaID, "active") + if err != nil { + t.Fatalf("Replica status error: %v", err) + } +} + +func TestReplicaExportLocations(t *testing.T) { + clients.RequireManilaReplicas(t) + + client, err := clients.NewSharedFileSystemV2Client() + if err != nil { + t.Fatalf("Unable to create a shared file system client: %v", err) + } + client.Microversion = replicasMicroversion + + share, err := CreateShare(t, client) + if err != nil { + t.Fatalf("Unable to create a share: %v", err) + } + + defer DeleteShare(t, client, share) + + replica, err := CreateReplica(t, client, share) + if err != nil { + t.Fatalf("Unable to create a replica: %v", err) + } + + defer DeleteReplica(t, client, replica) + + // this call should return empty list, since replica is not yet active + exportLocations, err := replicas.ListExportLocations(client, replica.ID).Extract() + if err != nil { + t.Errorf("Unable to list replica export locations: %v", err) + } + tools.PrintResource(t, exportLocations) + + opts := replicas.ListOpts{ + ShareID: share.ID, + } + pages, err := replicas.List(client, opts).AllPages() + th.AssertNoErr(t, err) + + allReplicas, err := replicas.ExtractReplicas(pages) + th.AssertNoErr(t, err) + + var activeReplicaID string + for _, v := range allReplicas { + if v.State == "active" && v.Status == "available" { + activeReplicaID = v.ID + } + } + + if activeReplicaID == "" { + t.Errorf("Unable to get active replica") + } + + exportLocations, err = replicas.ListExportLocations(client, activeReplicaID).Extract() + if err != nil { + t.Errorf("Unable to list replica export locations: %v", err) + } + tools.PrintResource(t, exportLocations) + + exportLocation, err := replicas.GetExportLocation(client, activeReplicaID, exportLocations[0].ID).Extract() + if err != nil { + t.Errorf("Unable to get replica export location: %v", err) + } + tools.PrintResource(t, exportLocation) + // unset CreatedAt and UpdatedAt + exportLocation.CreatedAt = time.Time{} + exportLocation.UpdatedAt = time.Time{} + th.AssertEquals(t, exportLocations[0], *exportLocation) +} + +func TestReplicaListDetail(t *testing.T) { + clients.RequireManilaReplicas(t) + + client, err := clients.NewSharedFileSystemV2Client() + if err != nil { + t.Fatalf("Unable to create a shared file system client: %v", err) + } + client.Microversion = replicasMicroversion + + share, err := CreateShare(t, client) + if err != nil { + t.Fatalf("Unable to create a share: %v", err) + } + + defer DeleteShare(t, client, share) + + replica, err := CreateReplica(t, client, share) + if err != nil { + t.Fatalf("Unable to create a replica: %v", err) + } + + defer DeleteReplica(t, client, replica) + + ss, err := ListShareReplicas(t, client, share.ID) + if err != nil { + t.Fatalf("Unable to list replicas: %v", err) + } + + for i := range ss { + tools.PrintResource(t, &ss[i]) + } +} + +func TestReplicaResetStatus(t *testing.T) { + clients.RequireManilaReplicas(t) + + client, err := clients.NewSharedFileSystemV2Client() + if err != nil { + t.Fatalf("Unable to create a shared file system client: %v", err) + } + client.Microversion = replicasMicroversion + + share, err := CreateShare(t, client) + if err != nil { + t.Fatalf("Unable to create a share: %v", err) + } + + defer DeleteShare(t, client, share) + + replica, err := CreateReplica(t, client, share) + if err != nil { + t.Fatalf("Unable to create a replica: %v", err) + } + + defer DeleteReplica(t, client, replica) + + resetStatusOpts := &replicas.ResetStatusOpts{ + Status: "error", + } + err = replicas.ResetStatus(client, replica.ID, resetStatusOpts).ExtractErr() + if err != nil { + t.Fatalf("Unable to reset a replica status: %v", err) + } + + // We need to wait till the Extend operation is done + _, err = waitForReplicaStatus(t, client, replica.ID, "error") + if err != nil { + t.Fatalf("Replica status error: %v", err) + } + + t.Logf("Replica %s status successfuly reset", replica.ID) +} + +// This test available only for cloud admins +func TestReplicaForceDelete(t *testing.T) { + clients.RequireManilaReplicas(t) + clients.RequireAdmin(t) + + client, err := clients.NewSharedFileSystemV2Client() + if err != nil { + t.Fatalf("Unable to create a shared file system client: %v", err) + } + client.Microversion = replicasMicroversion + + share, err := CreateShare(t, client) + if err != nil { + t.Fatalf("Unable to create a share: %v", err) + } + + defer DeleteShare(t, client, share) + + replica, err := CreateReplica(t, client, share) + if err != nil { + t.Fatalf("Unable to create a replica: %v", err) + } + + defer DeleteReplica(t, client, replica) + + err = replicas.ForceDelete(client, replica.ID).ExtractErr() + if err != nil { + t.Fatalf("Unable to force delete a replica: %v", err) + } + + _, err = waitForReplicaStatus(t, client, replica.ID, "deleted") + if err != nil { + t.Fatalf("Replica status error: %v", err) + } + + t.Logf("Replica %s was successfuly deleted", replica.ID) +} diff --git a/acceptance/openstack/sharedfilesystems/v2/schedulerstats_test.go b/acceptance/openstack/sharedfilesystems/v2/schedulerstats_test.go index fc2d17e5e8..aebe388672 100644 --- a/acceptance/openstack/sharedfilesystems/v2/schedulerstats_test.go +++ b/acceptance/openstack/sharedfilesystems/v2/schedulerstats_test.go @@ -14,8 +14,8 @@ import ( func TestSchedulerStatsList(t *testing.T) { client, err := clients.NewSharedFileSystemV2Client() - client.Microversion = "2.23" th.AssertNoErr(t, err) + client.Microversion = "2.23" allPages, err := schedulerstats.List(client, nil).AllPages() th.AssertNoErr(t, err) diff --git a/openstack/sharedfilesystems/v2/replicas/requests.go b/openstack/sharedfilesystems/v2/replicas/requests.go new file mode 100644 index 0000000000..3fa2896f1e --- /dev/null +++ b/openstack/sharedfilesystems/v2/replicas/requests.go @@ -0,0 +1,271 @@ +package replicas + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// CreateOptsBuilder allows extensions to add additional parameters to the +// Create request. +type CreateOptsBuilder interface { + ToReplicaCreateMap() (map[string]interface{}, error) +} + +// CreateOpts contains the options for create a Share Replica. This object is +// passed to replicas.Create function. For more information about these parameters, +// please refer to the Replica object, or the shared file systems API v2 +// documentation. +type CreateOpts struct { + // The UUID of the share from which to create a share replica. + ShareID string `json:"share_id" required:"true"` + // The UUID of the share network to which the share replica should + // belong to. + ShareNetworkID string `json:"share_network_id,omitempty"` + // The availability zone of the share replica. + AvailabilityZone string `json:"availability_zone,omitempty"` + // One or more scheduler hints key and value pairs as a dictionary of + // strings. Minimum supported microversion for SchedulerHints is 2.67. + SchedulerHints map[string]string `json:"scheduler_hints,omitempty"` +} + +// ToReplicaCreateMap assembles a request body based on the contents of a +// CreateOpts. +func (opts CreateOpts) ToReplicaCreateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "share_replica") +} + +// Create will create a new Share Replica based on the values in CreateOpts. To extract +// the Replica object from the response, call the Extract method on the +// CreateResult. +func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { + b, err := opts.ToReplicaCreateMap() + 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 +} + +// ListOpts holds options for listing Share Replicas. This object is passed to the +// replicas.List function. +type ListOpts struct { + // The UUID of the share. + ShareID string `q:"share_id"` + // Per page limit for share replicas + 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"` +} + +// ListOptsBuilder allows extensions to add additional parameters to the List +// request. +type ListOptsBuilder interface { + ToReplicaListQuery() (string, error) +} + +// ToReplicaListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToReplicaListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// List returns []Replica 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.ToReplicaListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + p := ReplicaPage{pagination.MarkerPageBase{PageResult: r}} + p.MarkerPageBase.Owner = p + return p + }) +} + +// ListDetail returns []Replica optionally limited by the conditions provided in ListOpts. +func ListDetail(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := listDetailURL(client) + if opts != nil { + query, err := opts.ToReplicaListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + p := ReplicaPage{pagination.MarkerPageBase{PageResult: r}} + p.MarkerPageBase.Owner = p + return p + }) +} + +// Delete will delete an existing Replica with the given UUID. +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 +} + +// Get will get a single share with given UUID +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 +} + +// ListExportLocations will list replicaID's export locations. +// Minimum supported microversion for ListExportLocations is 2.47. +func ListExportLocations(client *gophercloud.ServiceClient, id string) (r ListExportLocationsResult) { + resp, err := client.Get(listExportLocationsURL(client, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// GetExportLocation will get replicaID's export location by an ID. +// Minimum supported microversion for GetExportLocation is 2.47. +func GetExportLocation(client *gophercloud.ServiceClient, replicaID string, id string) (r GetExportLocationResult) { + resp, err := client.Get(getExportLocationURL(client, replicaID, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// PromoteOptsBuilder allows extensions to add additional parameters to the +// Promote request. +type PromoteOptsBuilder interface { + ToReplicaPromoteMap() (map[string]interface{}, error) +} + +// PromoteOpts contains options for promoteing a Replica to active replica state. +// This object is passed to the replicas.Promote function. +type PromoteOpts struct { + // The quiesce wait time in seconds used during replica promote. + // Minimum supported microversion for QuiesceWaitTime is 2.75. + QuiesceWaitTime int `json:"quiesce_wait_time,omitempty"` +} + +// ToReplicaPromoteMap assembles a request body based on the contents of a +// PromoteOpts. +func (opts PromoteOpts) ToReplicaPromoteMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "promote") +} + +// Promote will promote an existing Replica to active state. PromoteResult contains only the error. +// To extract it, call the ExtractErr method on the PromoteResult. +func Promote(client *gophercloud.ServiceClient, id string, opts PromoteOptsBuilder) (r PromoteResult) { + b, err := opts.ToReplicaPromoteMap() + 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 +} + +// Resync a replica with its active mirror. ResyncResult contains only the error. +// To extract it, call the ExtractErr method on the ResyncResult. +func Resync(client *gophercloud.ServiceClient, id string) (r ResyncResult) { + resp, err := client.Post(actionURL(client, id), map[string]interface{}{"resync": nil}, 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 { + ToReplicaResetStatusMap() (map[string]interface{}, error) +} + +// ResetStatusOpts contain options for updating a Share Replica status. This object is passed +// to the replicas.ResetStatus function. Administrator only. +type ResetStatusOpts struct { + // The status of a share replica. List of possible values: "available", + // "error", "creating", "deleting" or "error_deleting". + Status string `json:"status" required:"true"` +} + +// ToReplicaResetStatusMap assembles a request body based on the contents of an +// ResetStatusOpts. +func (opts ResetStatusOpts) ToReplicaResetStatusMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "reset_status") +} + +// ResetStatus will reset the Share Replica status with provided information. +// ResetStatusResult contains only the error. To extract it, call the ExtractErr +// method on the ResetStatusResult. +func ResetStatus(client *gophercloud.ServiceClient, id string, opts ResetStatusOptsBuilder) (r ResetStatusResult) { + b, err := opts.ToReplicaResetStatusMap() + 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 +} + +// ResetStateOptsBuilder allows extensions to add additional parameters to the +// ResetState request. +type ResetStateOptsBuilder interface { + ToReplicaResetStateMap() (map[string]interface{}, error) +} + +// ResetStateOpts contain options for updating a Share Replica state. This object is passed +// to the replicas.ResetState function. Administrator only. +type ResetStateOpts struct { + // The state of a share replica. List of possible values: "active", + // "in_sync", "out_of_sync" or "error". + State string `json:"replica_state" required:"true"` +} + +// ToReplicaResetStateMap assembles a request body based on the contents of an +// ResetStateOpts. +func (opts ResetStateOpts) ToReplicaResetStateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "reset_replica_state") +} + +// ResetState will reset the Share Replica state with provided information. +// ResetStateResult contains only the error. To extract it, call the ExtractErr +// method on the ResetStateResult. +func ResetState(client *gophercloud.ServiceClient, id string, opts ResetStateOptsBuilder) (r ResetStateResult) { + b, err := opts.ToReplicaResetStateMap() + 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 +} + +// ForceDelete force-deletes a Share Replica in any state. ForceDeleteResult +// contains only the error. To extract it, call the ExtractErr method on the +// ForceDeleteResult. Administrator only. +func ForceDelete(client *gophercloud.ServiceClient, id string) (r ForceDeleteResult) { + resp, err := client.Post(actionURL(client, id), map[string]interface{}{"force_delete": nil}, nil, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} diff --git a/openstack/sharedfilesystems/v2/replicas/results.go b/openstack/sharedfilesystems/v2/replicas/results.go new file mode 100644 index 0000000000..5ab0c0b8d0 --- /dev/null +++ b/openstack/sharedfilesystems/v2/replicas/results.go @@ -0,0 +1,272 @@ +package replicas + +import ( + "encoding/json" + "net/url" + "strconv" + "time" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +const ( + invalidMarker = "-1" +) + +// Replica contains all information associated with an OpenStack Share Replica. +type Replica struct { + // ID of the share replica + ID string `json:"id"` + // The availability zone of the share replica. + AvailabilityZone string `json:"availability_zone"` + // Indicates whether existing access rules will be cast to read/only. + CastRulesToReadonly bool `json:"cast_rules_to_readonly"` + // The host name of the share replica. + Host string `json:"host"` + // The UUID of the share to which a share replica belongs. + ShareID string `json:"share_id"` + // The UUID of the share network where the resource is exported to. + ShareNetworkID string `json:"share_network_id"` + // The UUID of the share server. + ShareServerID string `json:"share_server_id"` + // The share replica status. + Status string `json:"status"` + // The share replica state. + State string `json:"replica_state"` + // Timestamp when the replica was created. + CreatedAt time.Time `json:"-"` + // Timestamp when the replica was updated. + UpdatedAt time.Time `json:"-"` +} + +func (r *Replica) UnmarshalJSON(b []byte) error { + type tmp Replica + 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 = Replica(s.tmp) + + r.CreatedAt = time.Time(s.CreatedAt) + r.UpdatedAt = time.Time(s.UpdatedAt) + + return nil +} + +type commonResult struct { + gophercloud.Result +} + +// Extract will get the Replica object from the commonResul.t +func (r commonResult) Extract() (*Replica, error) { + var s struct { + Replica *Replica `json:"share_replica"` + } + err := r.ExtractInto(&s) + return s.Replica, err +} + +// CreateResult contains the response body and error from a Create request. +type CreateResult struct { + commonResult +} + +// ReplicaPage is a pagination.pager that is returned from a call to the List function. +type ReplicaPage struct { + pagination.MarkerPageBase +} + +// NextPageURL generates the URL for the page of results after this one. +func (r ReplicaPage) NextPageURL() (string, error) { + currentURL := r.URL + mark, err := r.Owner.LastMarker() + if err != nil { + return "", err + } + if mark == invalidMarker { + return "", nil + } + + q := currentURL.Query() + q.Set("offset", mark) + currentURL.RawQuery = q.Encode() + return currentURL.String(), nil +} + +// LastMarker returns the last offset in a ListResult. +func (r ReplicaPage) LastMarker() (string, error) { + replicas, err := ExtractReplicas(r) + if err != nil { + return invalidMarker, err + } + if len(replicas) == 0 { + return invalidMarker, nil + } + + u, err := url.Parse(r.URL.String()) + if err != nil { + return invalidMarker, err + } + queryParams := u.Query() + offset := queryParams.Get("offset") + limit := queryParams.Get("limit") + + // Limit is not present, only one page required + if limit == "" { + return invalidMarker, nil + } + + iOffset := 0 + if offset != "" { + iOffset, err = strconv.Atoi(offset) + if err != nil { + return invalidMarker, err + } + } + iLimit, err := strconv.Atoi(limit) + if err != nil { + return invalidMarker, err + } + iOffset = iOffset + iLimit + offset = strconv.Itoa(iOffset) + + return offset, nil +} + +// IsEmpty satisifies the IsEmpty method of the Page interface. +func (r ReplicaPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + + replicas, err := ExtractReplicas(r) + return len(replicas) == 0, err +} + +// ExtractReplicas extracts and returns a Replica slice. It is used while +// iterating over a replicas.List call. +func ExtractReplicas(r pagination.Page) ([]Replica, error) { + var s struct { + Replicas []Replica `json:"share_replicas"` + } + + err := (r.(ReplicaPage)).ExtractInto(&s) + + return s.Replicas, err +} + +// DeleteResult contains the response body and error from a Delete request. +type DeleteResult struct { + gophercloud.ErrResult +} + +// GetResult contains the response body and error from a Get request. +type GetResult struct { + commonResult +} + +// ListExportLocationsResult contains the result body and error from a +// ListExportLocations request. +type ListExportLocationsResult struct { + gophercloud.Result +} + +// GetExportLocationResult contains the result body and error from a +// GetExportLocation request. +type GetExportLocationResult struct { + gophercloud.Result +} + +// ExportLocation contains all information associated with a share export location +type ExportLocation struct { + // The share replica export location UUID. + ID string `json:"id"` + // The export location path that should be used for mount operation. + Path string `json:"path"` + // The UUID of the share instance that this export location belongs to. + ShareInstanceID string `json:"share_instance_id"` + // Defines purpose of an export location. If set to true, then it is + // expected to be used for service needs and by administrators only. If + // it is set to false, then this export location can be used by end users. + IsAdminOnly bool `json:"is_admin_only"` + // Drivers may use this field to identify which export locations are + // most efficient and should be used preferentially by clients. + // By default it is set to false value. New in version 2.14. + Preferred bool `json:"preferred"` + // The availability zone of the share replica. + AvailabilityZone string `json:"availability_zone"` + // The share replica state. + State string `json:"replica_state"` + // Timestamp when the export location was created. + CreatedAt time.Time `json:"-"` + // Timestamp when the export location was updated. + UpdatedAt time.Time `json:"-"` +} + +func (r *ExportLocation) UnmarshalJSON(b []byte) error { + type tmp ExportLocation + 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 = ExportLocation(s.tmp) + + r.CreatedAt = time.Time(s.CreatedAt) + r.UpdatedAt = time.Time(s.UpdatedAt) + + return nil +} + +// Extract will get the Export Locations from the ListExportLocationsResult +func (r ListExportLocationsResult) Extract() ([]ExportLocation, error) { + var s struct { + ExportLocations []ExportLocation `json:"export_locations"` + } + err := r.ExtractInto(&s) + return s.ExportLocations, err +} + +// Extract will get the Export Location from the GetExportLocationResult +func (r GetExportLocationResult) Extract() (*ExportLocation, error) { + var s struct { + ExportLocation *ExportLocation `json:"export_location"` + } + err := r.ExtractInto(&s) + return s.ExportLocation, err +} + +// PromoteResult contains the error from an Promote request. +type PromoteResult struct { + gophercloud.ErrResult +} + +// ResyncResult contains the error from a Resync request. +type ResyncResult struct { + gophercloud.ErrResult +} + +// ResetStatusResult contains the error from a ResetStatus request. +type ResetStatusResult struct { + gophercloud.ErrResult +} + +// ResetStateResult contains the error from a ResetState request. +type ResetStateResult struct { + gophercloud.ErrResult +} + +// ForceDeleteResult contains the error from a ForceDelete request. +type ForceDeleteResult struct { + gophercloud.ErrResult +} diff --git a/openstack/sharedfilesystems/v2/replicas/testing/fixtures.go b/openstack/sharedfilesystems/v2/replicas/testing/fixtures.go new file mode 100644 index 0000000000..edfd5698e1 --- /dev/null +++ b/openstack/sharedfilesystems/v2/replicas/testing/fixtures.go @@ -0,0 +1,356 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/testhelper/client" +) + +const ( + shareEndpoint = "/share-replicas" + replicaID = "3b9c33e8-b136-45c6-84a6-019c8db1d550" +) + +var createRequest = `{ + "share_replica": { + "share_id": "65a34695-f9e5-4eea-b48d-a0b261d82943", + "availability_zone": "zone-1" + } +} +` + +var createResponse = `{ + "share_replica": { + "id": "3b9c33e8-b136-45c6-84a6-019c8db1d550", + "share_id": "65a34695-f9e5-4eea-b48d-a0b261d82943", + "availability_zone": "zone-1", + "created_at": "2023-05-26T12:32:56.391337", + "status": "creating", + "share_network_id": "ca0163c8-3941-4420-8b01-41517e19e366", + "share_server_id": null, + "replica_state": null, + "updated_at": null + } +} +` + +// MockCreateResponse creates a mock response +func MockCreateResponse(t *testing.T) { + th.Mux.HandleFunc(shareEndpoint, func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestHeader(t, r, "Content-Type", "application/json") + th.TestHeader(t, r, "Accept", "application/json") + th.TestHeader(t, r, "X-OpenStack-Manila-API-Version", "2.11") + th.TestJSONRequest(t, r, createRequest) + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusAccepted) + fmt.Fprintf(w, createResponse) + }) +} + +// MockDeleteResponse creates a mock delete response +func MockDeleteResponse(t *testing.T) { + th.Mux.HandleFunc(shareEndpoint+"/"+replicaID, func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "DELETE") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestHeader(t, r, "X-OpenStack-Manila-API-Version", "2.11") + w.WriteHeader(http.StatusAccepted) + }) +} + +var promoteRequest = `{ + "promote": { + "quiesce_wait_time": 30 + } +} +` + +func MockPromoteResponse(t *testing.T) { + th.Mux.HandleFunc(shareEndpoint+"/"+replicaID+"/action", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestHeader(t, r, "Content-Type", "application/json") + th.TestHeader(t, r, "Accept", "application/json") + th.TestHeader(t, r, "X-OpenStack-Manila-API-Version", "2.11") + th.TestJSONRequest(t, r, promoteRequest) + w.WriteHeader(http.StatusAccepted) + }) +} + +var resyncRequest = `{ + "resync": null +} +` + +func MockResyncResponse(t *testing.T) { + th.Mux.HandleFunc(shareEndpoint+"/"+replicaID+"/action", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestHeader(t, r, "Content-Type", "application/json") + th.TestHeader(t, r, "Accept", "application/json") + th.TestHeader(t, r, "X-OpenStack-Manila-API-Version", "2.11") + th.TestJSONRequest(t, r, resyncRequest) + w.WriteHeader(http.StatusAccepted) + }) +} + +var resetStatusRequest = `{ + "reset_status": { + "status": "available" + } +} +` + +func MockResetStatusResponse(t *testing.T) { + th.Mux.HandleFunc(shareEndpoint+"/"+replicaID+"/action", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestHeader(t, r, "Content-Type", "application/json") + th.TestHeader(t, r, "Accept", "application/json") + th.TestHeader(t, r, "X-OpenStack-Manila-API-Version", "2.11") + th.TestJSONRequest(t, r, resetStatusRequest) + w.WriteHeader(http.StatusAccepted) + }) +} + +var resetStateRequest = `{ + "reset_replica_state": { + "replica_state": "active" + } +} +` + +func MockResetStateResponse(t *testing.T) { + th.Mux.HandleFunc(shareEndpoint+"/"+replicaID+"/action", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestHeader(t, r, "Content-Type", "application/json") + th.TestHeader(t, r, "Accept", "application/json") + th.TestHeader(t, r, "X-OpenStack-Manila-API-Version", "2.11") + th.TestJSONRequest(t, r, resetStateRequest) + w.WriteHeader(http.StatusAccepted) + }) +} + +var deleteRequest = `{ + "force_delete": null +} +` + +func MockForceDeleteResponse(t *testing.T) { + th.Mux.HandleFunc(shareEndpoint+"/"+replicaID+"/action", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestHeader(t, r, "Content-Type", "application/json") + th.TestHeader(t, r, "Accept", "application/json") + th.TestHeader(t, r, "X-OpenStack-Manila-API-Version", "2.11") + th.TestJSONRequest(t, r, deleteRequest) + w.WriteHeader(http.StatusAccepted) + }) +} + +var getResponse = `{ + "share_replica": { + "id": "3b9c33e8-b136-45c6-84a6-019c8db1d550", + "share_id": "65a34695-f9e5-4eea-b48d-a0b261d82943", + "availability_zone": "zone-1", + "created_at": "2023-05-26T12:32:56.391337", + "status": "available", + "share_network_id": "ca0163c8-3941-4420-8b01-41517e19e366", + "share_server_id": "5ccc1b0c-334a-4e46-81e6-b52e03223060", + "replica_state": "active", + "updated_at": "2023-05-26T12:33:28.265716" + } +} +` + +// MockGetResponse creates a mock get response +func MockGetResponse(t *testing.T) { + th.Mux.HandleFunc(shareEndpoint+"/"+replicaID, func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestHeader(t, r, "X-OpenStack-Manila-API-Version", "2.11") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, getResponse) + }) +} + +var listResponse = `{ + "share_replicas": [ + { + "id": "3b9c33e8-b136-45c6-84a6-019c8db1d550", + "share_id": "65a34695-f9e5-4eea-b48d-a0b261d82943", + "status": "available", + "replica_state": "active" + }, + { + "id": "4b70c2e2-eec7-4699-880d-4da9051ca162", + "share_id": "65a34695-f9e5-4eea-b48d-a0b261d82943", + "status": "available", + "replica_state": "out_of_sync" + }, + { + "id": "920bb037-bdd7-48a1-98f0-1aa1787ca3eb", + "share_id": "65a34695-f9e5-4eea-b48d-a0b261d82943", + "status": "available", + "replica_state": "in_sync" + } + ] +} +` + +var listEmptyResponse = `{"share_replicas": []}` + +// MockListResponse creates a mock detailed-list response +func MockListResponse(t *testing.T) { + th.Mux.HandleFunc(shareEndpoint, func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestHeader(t, r, "X-OpenStack-Manila-API-Version", "2.11") + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + r.ParseForm() + marker := r.Form.Get("offset") + shareID := r.Form.Get("share_id") + if shareID != "65a34695-f9e5-4eea-b48d-a0b261d82943" { + th.AssertNoErr(t, fmt.Errorf("unexpected share_id")) + } + + switch marker { + case "": + fmt.Fprint(w, listResponse) + default: + fmt.Fprint(w, listEmptyResponse) + } + }) +} + +var listDetailResponse = `{ + "share_replicas": [ + { + "id": "3b9c33e8-b136-45c6-84a6-019c8db1d550", + "share_id": "65a34695-f9e5-4eea-b48d-a0b261d82943", + "availability_zone": "zone-1", + "created_at": "2023-05-26T12:32:56.391337", + "status": "available", + "share_network_id": "ca0163c8-3941-4420-8b01-41517e19e366", + "share_server_id": "5ccc1b0c-334a-4e46-81e6-b52e03223060", + "replica_state": "active", + "updated_at": "2023-05-26T12:33:28.265716" + }, + { + "id": "4b70c2e2-eec7-4699-880d-4da9051ca162", + "share_id": "65a34695-f9e5-4eea-b48d-a0b261d82943", + "availability_zone": "zone-2", + "created_at": "2023-05-26T11:59:38.313089", + "status": "available", + "share_network_id": "ca0163c8-3941-4420-8b01-41517e19e366", + "share_server_id": "81aa586e-3a03-4f92-98bd-807d87a61c1a", + "replica_state": "out_of_sync", + "updated_at": "2023-05-26T12:00:04.321081" + }, + { + "id": "920bb037-bdd7-48a1-98f0-1aa1787ca3eb", + "share_id": "65a34695-f9e5-4eea-b48d-a0b261d82943", + "availability_zone": "zone-1", + "created_at": "2023-05-26T12:32:45.751834", + "status": "available", + "share_network_id": "ca0163c8-3941-4420-8b01-41517e19e366", + "share_server_id": "b87ea601-7d4c-47f3-8956-6876b7a6b6db", + "replica_state": "in_sync", + "updated_at": "2023-05-26T12:36:04.110328" + } + ] +} +` + +var listDetailEmptyResponse = `{"share_replicas": []}` + +// MockListDetailResponse creates a mock detailed-list response +func MockListDetailResponse(t *testing.T) { + th.Mux.HandleFunc(shareEndpoint+"/detail", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestHeader(t, r, "X-OpenStack-Manila-API-Version", "2.11") + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + r.ParseForm() + marker := r.Form.Get("offset") + shareID := r.Form.Get("share_id") + if shareID != "65a34695-f9e5-4eea-b48d-a0b261d82943" { + th.AssertNoErr(t, fmt.Errorf("unexpected share_id")) + } + + switch marker { + case "": + fmt.Fprint(w, listDetailResponse) + default: + fmt.Fprint(w, listDetailEmptyResponse) + } + }) +} + +var listExportLocationsResponse = `{ + "export_locations": [ + { + "id": "3fc02d3c-da47-42a2-88b8-2d48f8c276bd", + "path": "192.168.1.123:/var/lib/manila/mnt/share-3b9c33e8-b136-45c6-84a6-019c8db1d550", + "preferred": true, + "replica_state": "active", + "availability_zone": "zone-1" + }, + { + "id": "ae73e762-e8b9-4aad-aad3-23afb7cd6825", + "path": "192.168.1.124:/var/lib/manila/mnt/share-3b9c33e8-b136-45c6-84a6-019c8db1d550", + "preferred": false, + "replica_state": "active", + "availability_zone": "zone-1" + } + ] +} +` + +// MockListExportLocationsResponse creates a mock get export locations response +func MockListExportLocationsResponse(t *testing.T) { + th.Mux.HandleFunc(shareEndpoint+"/"+replicaID+"/export-locations", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + w.Header().Add("Content-Type", "application/json") + th.TestHeader(t, r, "X-OpenStack-Manila-API-Version", "2.47") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, listExportLocationsResponse) + }) +} + +var getExportLocationResponse = `{ + "export_location": { + "id": "ae73e762-e8b9-4aad-aad3-23afb7cd6825", + "path": "192.168.1.124:/var/lib/manila/mnt/share-3b9c33e8-b136-45c6-84a6-019c8db1d550", + "preferred": false, + "created_at": "2023-05-26T12:44:33.987960", + "updated_at": "2023-05-26T12:44:33.958363", + "replica_state": "active", + "availability_zone": "zone-1" + } +} +` + +// MockGetExportLocationResponse creates a mock get export location response +func MockGetExportLocationResponse(t *testing.T) { + th.Mux.HandleFunc(shareEndpoint+"/"+replicaID+"/export-locations/ae73e762-e8b9-4aad-aad3-23afb7cd6825", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + w.Header().Add("Content-Type", "application/json") + th.TestHeader(t, r, "X-OpenStack-Manila-API-Version", "2.47") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, getExportLocationResponse) + }) +} diff --git a/openstack/sharedfilesystems/v2/replicas/testing/request_test.go b/openstack/sharedfilesystems/v2/replicas/testing/request_test.go new file mode 100644 index 0000000000..df070061cc --- /dev/null +++ b/openstack/sharedfilesystems/v2/replicas/testing/request_test.go @@ -0,0 +1,269 @@ +package testing + +import ( + "testing" + "time" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/replicas" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +func getClient(microVersion string) *gophercloud.ServiceClient { + c := client.ServiceClient() + c.Type = "sharev2" + c.Microversion = microVersion + return c +} + +func TestCreate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockCreateResponse(t) + + options := &replicas.CreateOpts{ + ShareID: "65a34695-f9e5-4eea-b48d-a0b261d82943", + AvailabilityZone: "zone-1", + } + actual, err := replicas.Create(getClient("2.11"), options).Extract() + + expected := &replicas.Replica{ + ID: "3b9c33e8-b136-45c6-84a6-019c8db1d550", + ShareID: "65a34695-f9e5-4eea-b48d-a0b261d82943", + AvailabilityZone: "zone-1", + Status: "creating", + ShareNetworkID: "ca0163c8-3941-4420-8b01-41517e19e366", + CreatedAt: time.Date(2023, time.May, 26, 12, 32, 56, 391337000, time.UTC), //"2023-05-26T12:32:56.391337", + } + + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, expected, actual) +} + +func TestDelete(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockDeleteResponse(t) + + result := replicas.Delete(getClient("2.11"), replicaID) + th.AssertNoErr(t, result.Err) +} + +func TestForceDeleteSuccess(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockForceDeleteResponse(t) + + err := replicas.ForceDelete(getClient("2.11"), replicaID).ExtractErr() + th.AssertNoErr(t, err) +} + +func TestGet(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockGetResponse(t) + + actual, err := replicas.Get(getClient("2.11"), replicaID).Extract() + + expected := &replicas.Replica{ + AvailabilityZone: "zone-1", + ShareNetworkID: "ca0163c8-3941-4420-8b01-41517e19e366", + ShareServerID: "5ccc1b0c-334a-4e46-81e6-b52e03223060", + ShareID: "65a34695-f9e5-4eea-b48d-a0b261d82943", + ID: replicaID, + Status: "available", + State: "active", + CreatedAt: time.Date(2023, time.May, 26, 12, 32, 56, 391337000, time.UTC), + UpdatedAt: time.Date(2023, time.May, 26, 12, 33, 28, 265716000, time.UTC), + } + + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, expected, actual) +} + +func TestList(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockListResponse(t) + + listOpts := &replicas.ListOpts{ + ShareID: "65a34695-f9e5-4eea-b48d-a0b261d82943", + } + allPages, err := replicas.List(getClient("2.11"), listOpts).AllPages() + th.AssertNoErr(t, err) + + actual, err := replicas.ExtractReplicas(allPages) + th.AssertNoErr(t, err) + + expected := []replicas.Replica{ + { + ID: replicaID, + ShareID: "65a34695-f9e5-4eea-b48d-a0b261d82943", + Status: "available", + State: "active", + }, + { + ID: "4b70c2e2-eec7-4699-880d-4da9051ca162", + ShareID: "65a34695-f9e5-4eea-b48d-a0b261d82943", + Status: "available", + State: "out_of_sync", + }, + { + ID: "920bb037-bdd7-48a1-98f0-1aa1787ca3eb", + ShareID: "65a34695-f9e5-4eea-b48d-a0b261d82943", + Status: "available", + State: "in_sync", + }, + } + + th.AssertDeepEquals(t, expected, actual) +} + +func TestListDetail(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockListDetailResponse(t) + + listOpts := &replicas.ListOpts{ + ShareID: "65a34695-f9e5-4eea-b48d-a0b261d82943", + } + allPages, err := replicas.ListDetail(getClient("2.11"), listOpts).AllPages() + th.AssertNoErr(t, err) + + actual, err := replicas.ExtractReplicas(allPages) + th.AssertNoErr(t, err) + + expected := []replicas.Replica{ + { + AvailabilityZone: "zone-1", + ShareNetworkID: "ca0163c8-3941-4420-8b01-41517e19e366", + ShareServerID: "5ccc1b0c-334a-4e46-81e6-b52e03223060", + ShareID: "65a34695-f9e5-4eea-b48d-a0b261d82943", + ID: replicaID, + Status: "available", + State: "active", + CreatedAt: time.Date(2023, time.May, 26, 12, 32, 56, 391337000, time.UTC), + UpdatedAt: time.Date(2023, time.May, 26, 12, 33, 28, 265716000, time.UTC), + }, + { + AvailabilityZone: "zone-2", + ShareNetworkID: "ca0163c8-3941-4420-8b01-41517e19e366", + ShareServerID: "81aa586e-3a03-4f92-98bd-807d87a61c1a", + ShareID: "65a34695-f9e5-4eea-b48d-a0b261d82943", + ID: "4b70c2e2-eec7-4699-880d-4da9051ca162", + Status: "available", + State: "out_of_sync", + CreatedAt: time.Date(2023, time.May, 26, 11, 59, 38, 313089000, time.UTC), + UpdatedAt: time.Date(2023, time.May, 26, 12, 00, 04, 321081000, time.UTC), + }, + { + AvailabilityZone: "zone-1", + ShareNetworkID: "ca0163c8-3941-4420-8b01-41517e19e366", + ShareServerID: "b87ea601-7d4c-47f3-8956-6876b7a6b6db", + ShareID: "65a34695-f9e5-4eea-b48d-a0b261d82943", + ID: "920bb037-bdd7-48a1-98f0-1aa1787ca3eb", + Status: "available", + State: "in_sync", + CreatedAt: time.Date(2023, time.May, 26, 12, 32, 45, 751834000, time.UTC), + UpdatedAt: time.Date(2023, time.May, 26, 12, 36, 04, 110328000, time.UTC), + }, + } + + th.AssertDeepEquals(t, expected, actual) +} + +func TestListExportLocationsSuccess(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockListExportLocationsResponse(t) + + actual, err := replicas.ListExportLocations(getClient("2.47"), replicaID).Extract() + + expected := []replicas.ExportLocation{ + { + ID: "3fc02d3c-da47-42a2-88b8-2d48f8c276bd", + Path: "192.168.1.123:/var/lib/manila/mnt/share-3b9c33e8-b136-45c6-84a6-019c8db1d550", + Preferred: true, + State: "active", + AvailabilityZone: "zone-1", + }, + { + ID: "ae73e762-e8b9-4aad-aad3-23afb7cd6825", + Path: "192.168.1.124:/var/lib/manila/mnt/share-3b9c33e8-b136-45c6-84a6-019c8db1d550", + Preferred: false, + State: "active", + AvailabilityZone: "zone-1", + }, + } + + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, expected, actual) +} + +func TestGetExportLocationSuccess(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockGetExportLocationResponse(t) + + s, err := replicas.GetExportLocation(getClient("2.47"), replicaID, "ae73e762-e8b9-4aad-aad3-23afb7cd6825").Extract() + + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, s, &replicas.ExportLocation{ + Path: "192.168.1.124:/var/lib/manila/mnt/share-3b9c33e8-b136-45c6-84a6-019c8db1d550", + ID: "ae73e762-e8b9-4aad-aad3-23afb7cd6825", + Preferred: false, + State: "active", + AvailabilityZone: "zone-1", + CreatedAt: time.Date(2023, time.May, 26, 12, 44, 33, 987960000, time.UTC), + UpdatedAt: time.Date(2023, time.May, 26, 12, 44, 33, 958363000, time.UTC), + }) +} + +func TestResetStatusSuccess(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockResetStatusResponse(t) + + err := replicas.ResetStatus(getClient("2.11"), replicaID, &replicas.ResetStatusOpts{Status: "available"}).ExtractErr() + th.AssertNoErr(t, err) +} + +func TestResetStateSuccess(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockResetStateResponse(t) + + err := replicas.ResetState(getClient("2.11"), replicaID, &replicas.ResetStateOpts{State: "active"}).ExtractErr() + th.AssertNoErr(t, err) +} + +func TestResyncSuccess(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockResyncResponse(t) + + err := replicas.Resync(getClient("2.11"), replicaID).ExtractErr() + th.AssertNoErr(t, err) +} + +func TestPromoteSuccess(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockPromoteResponse(t) + + err := replicas.Promote(getClient("2.11"), replicaID, &replicas.PromoteOpts{QuiesceWaitTime: 30}).ExtractErr() + th.AssertNoErr(t, err) +} diff --git a/openstack/sharedfilesystems/v2/replicas/urls.go b/openstack/sharedfilesystems/v2/replicas/urls.go new file mode 100644 index 0000000000..99fa60416d --- /dev/null +++ b/openstack/sharedfilesystems/v2/replicas/urls.go @@ -0,0 +1,35 @@ +package replicas + +import "github.com/gophercloud/gophercloud" + +func createURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("share-replicas") +} + +func listURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("share-replicas") +} + +func listDetailURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("share-replicas", "detail") +} + +func deleteURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("share-replicas", id) +} + +func getURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("share-replicas", id) +} + +func listExportLocationsURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("share-replicas", id, "export-locations") +} + +func getExportLocationURL(c *gophercloud.ServiceClient, replicaID, id string) string { + return c.ServiceURL("share-replicas", replicaID, "export-locations", id) +} + +func actionURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("share-replicas", id, "action") +} From f25ec5f267867c8a33f7093ad44c2a1fa4a4bbc0 Mon Sep 17 00:00:00 2001 From: kayrus <kay.diam@gmail.com> Date: Wed, 14 Jun 2023 22:18:37 +0200 Subject: [PATCH 263/360] Fix some share replicas comments and introduce ExtractReplicasInto call --- .../sharedfilesystems/v2/replicas/requests.go | 2 +- .../sharedfilesystems/v2/replicas/results.go | 20 ++++++++++--------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/openstack/sharedfilesystems/v2/replicas/requests.go b/openstack/sharedfilesystems/v2/replicas/requests.go index 3fa2896f1e..6810f2e5c7 100644 --- a/openstack/sharedfilesystems/v2/replicas/requests.go +++ b/openstack/sharedfilesystems/v2/replicas/requests.go @@ -51,7 +51,7 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create } // ListOpts holds options for listing Share Replicas. This object is passed to the -// replicas.List function. +// replicas.List or replicas.ListDetail functions. type ListOpts struct { // The UUID of the share. ShareID string `q:"share_id"` diff --git a/openstack/sharedfilesystems/v2/replicas/results.go b/openstack/sharedfilesystems/v2/replicas/results.go index 5ab0c0b8d0..bf53de3d0b 100644 --- a/openstack/sharedfilesystems/v2/replicas/results.go +++ b/openstack/sharedfilesystems/v2/replicas/results.go @@ -63,7 +63,7 @@ type commonResult struct { gophercloud.Result } -// Extract will get the Replica object from the commonResul.t +// Extract will get the Replica object from the commonResult. func (r commonResult) Extract() (*Replica, error) { var s struct { Replica *Replica `json:"share_replica"` @@ -149,16 +149,18 @@ func (r ReplicaPage) IsEmpty() (bool, error) { return len(replicas) == 0, err } -// ExtractReplicas extracts and returns a Replica slice. It is used while -// iterating over a replicas.List call. +// ExtractReplicas extracts and returns Replicas. It is used while iterating +// over a replicas.List or replicas.ListDetail calls. func ExtractReplicas(r pagination.Page) ([]Replica, error) { - var s struct { - Replicas []Replica `json:"share_replicas"` - } - - err := (r.(ReplicaPage)).ExtractInto(&s) + var s []Replica + err := ExtractReplicasInto(r, &s) + return s, err +} - return s.Replicas, err +// ExtractReplicasInto similar to ExtractReplicas but operates on a `list` of +// replicas. +func ExtractReplicasInto(r pagination.Page, v interface{}) error { + return r.(ReplicaPage).Result.ExtractIntoSlicePtr(v, "share_replicas") } // DeleteResult contains the response body and error from a Delete request. From c779dc2343f26bb9ae46be2b5d9e53695c5adaca Mon Sep 17 00:00:00 2001 From: kayrus <kay.diam@gmail.com> Date: Wed, 14 Jun 2023 23:22:53 +0200 Subject: [PATCH 264/360] [manila]: implement share transfer API --- acceptance/clients/conditions.go | 2 +- .../sharedfilesystems/v2/sharetransfers.go | 46 ++++ .../v2/sharetransfers_test.go | 67 ++++++ .../v2/sharetransfers/requests.go | 169 ++++++++++++++ .../v2/sharetransfers/results.go | 170 ++++++++++++++ .../v2/sharetransfers/testing/fixtures.go | 207 ++++++++++++++++++ .../sharetransfers/testing/requests_test.go | 112 ++++++++++ .../v2/sharetransfers/urls.go | 27 +++ 8 files changed, 799 insertions(+), 1 deletion(-) create mode 100644 acceptance/openstack/sharedfilesystems/v2/sharetransfers.go create mode 100644 acceptance/openstack/sharedfilesystems/v2/sharetransfers_test.go create mode 100644 openstack/sharedfilesystems/v2/sharetransfers/requests.go create mode 100644 openstack/sharedfilesystems/v2/sharetransfers/results.go create mode 100644 openstack/sharedfilesystems/v2/sharetransfers/testing/fixtures.go create mode 100644 openstack/sharedfilesystems/v2/sharetransfers/testing/requests_test.go create mode 100644 openstack/sharedfilesystems/v2/sharetransfers/urls.go diff --git a/acceptance/clients/conditions.go b/acceptance/clients/conditions.go index ab33cb972c..44522c2308 100644 --- a/acceptance/clients/conditions.go +++ b/acceptance/clients/conditions.go @@ -147,7 +147,7 @@ func IsReleasesAbove(t *testing.T, release string) bool { func IsReleasesBelow(t *testing.T, release string) bool { current_branch := getReleaseFromEnv(t) - if current_branch != "master" && current_branch < release { + if current_branch != "master" || current_branch < release { return true } t.Logf("Target release %s is above the current branch %s", release, current_branch) diff --git a/acceptance/openstack/sharedfilesystems/v2/sharetransfers.go b/acceptance/openstack/sharedfilesystems/v2/sharetransfers.go new file mode 100644 index 0000000000..e7678cf455 --- /dev/null +++ b/acceptance/openstack/sharedfilesystems/v2/sharetransfers.go @@ -0,0 +1,46 @@ +package v2 + +import ( + "fmt" + "testing" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/shares" + "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/sharetransfers" +) + +func CreateTransferRequest(t *testing.T, client *gophercloud.ServiceClient, share *shares.Share, name string) (*sharetransfers.Transfer, error) { + opts := sharetransfers.CreateOpts{ + ShareID: share.ID, + Name: name, + } + transfer, err := sharetransfers.Create(client, opts).Extract() + if err != nil { + return nil, fmt.Errorf("failed to create a share transfer request: %s", err) + } + + return transfer, nil +} + +func AcceptTransfer(t *testing.T, client *gophercloud.ServiceClient, transferRequest *sharetransfers.Transfer) error { + opts := sharetransfers.AcceptOpts{ + AuthKey: transferRequest.AuthKey, + ClearAccessRules: true, + } + err := sharetransfers.Accept(client, transferRequest.ID, opts).ExtractErr() + if err != nil { + return fmt.Errorf("failed to accept a share transfer request: %s", err) + } + + return nil +} + +func DeleteTransferRequest(t *testing.T, client *gophercloud.ServiceClient, transfer *sharetransfers.Transfer) { + err := sharetransfers.Delete(client, transfer.ID).ExtractErr() + if err != nil { + if _, ok := err.(gophercloud.ErrDefault404); ok { + return + } + t.Errorf("Unable to delete share transfer %s: %v", transfer.ID, err) + } +} diff --git a/acceptance/openstack/sharedfilesystems/v2/sharetransfers_test.go b/acceptance/openstack/sharedfilesystems/v2/sharetransfers_test.go new file mode 100644 index 0000000000..8e3b70df48 --- /dev/null +++ b/acceptance/openstack/sharedfilesystems/v2/sharetransfers_test.go @@ -0,0 +1,67 @@ +//go:build acceptance || share || transfers +// +build acceptance share transfers + +package v2 + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" + th "github.com/gophercloud/gophercloud/testhelper" + + "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/sharetransfers" +) + +// minimal microversion for the share transfers +const shareTransfersMicroversion = "2.77" + +func TestTransferRequestCRUD(t *testing.T) { + clients.SkipReleasesBelow(t, "master") + + client, err := clients.NewSharedFileSystemV2Client() + if err != nil { + t.Fatalf("Unable to create a shared file system client: %v", err) + } + client.Microversion = shareTransfersMicroversion + + share, err := CreateShare(t, client) + if err != nil { + t.Fatalf("Unable to create a share: %v", err) + } + + defer DeleteShare(t, client, share) + + // Create transfers request to a new tenant + trName := "123" + transferRequest, err := CreateTransferRequest(t, client, share, trName) + th.AssertNoErr(t, err) + defer DeleteTransferRequest(t, client, transferRequest) + + // list transfer requests + allTransferRequestsPages, err := sharetransfers.ListDetail(client, nil).AllPages() + th.AssertNoErr(t, err) + + allTransferRequests, err := sharetransfers.ExtractTransfers(allTransferRequestsPages) + th.AssertNoErr(t, err) + + // finding the transfer request + var foundRequest bool + for _, tr := range allTransferRequests { + tools.PrintResource(t, &tr) + if tr.ResourceID == share.ID && tr.Name == trName && !tr.Accepted { + foundRequest = true + } + } + th.AssertEquals(t, foundRequest, true) + + // checking get + tr, err := sharetransfers.Get(client, transferRequest.ID).Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, transferRequest.ID == tr.ID, true) + + // Accept Share Transfer Request + err = AcceptTransfer(t, client, transferRequest) + th.AssertNoErr(t, err) +} diff --git a/openstack/sharedfilesystems/v2/sharetransfers/requests.go b/openstack/sharedfilesystems/v2/sharetransfers/requests.go new file mode 100644 index 0000000000..40ef8e7dbb --- /dev/null +++ b/openstack/sharedfilesystems/v2/sharetransfers/requests.go @@ -0,0 +1,169 @@ +package sharetransfers + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// CreateOptsBuilder allows extensions to add additional parameters to the +// Create request. +type CreateOptsBuilder interface { + ToTransferCreateMap() (map[string]interface{}, error) +} + +// CreateOpts contains options for a Share transfer. +type CreateOpts struct { + // The ID of the share to transfer. + ShareID string `json:"share_id" required:"true"` + + // The name of the share transfer. + Name string `json:"name,omitempty"` +} + +// ToCreateMap assembles a request body based on the contents of a +// TransferOpts. +func (opts CreateOpts) ToTransferCreateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "transfer") +} + +// Create will create a share tranfer request based on the values in CreateOpts. +func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { + b, err := opts.ToTransferCreateMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Post(transferURL(client), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// AcceptOpts contains options for a Share transfer accept reqeust. +type AcceptOpts struct { + // The auth key of the share transfer to accept. + AuthKey string `json:"auth_key" required:"true"` + + // Whether to clear access rules when accept the share. + ClearAccessRules bool `json:"clear_access_rules,omitempty"` +} + +// ToAcceptMap assembles a request body based on the contents of a +// AcceptOpts. +func (opts AcceptOpts) ToAcceptMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "accept") +} + +// Accept will accept a share tranfer request based on the values in AcceptOpts. +func Accept(client *gophercloud.ServiceClient, id string, opts AcceptOpts) (r AcceptResult) { + b, err := opts.ToAcceptMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Post(acceptURL(client, id), b, nil, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// Delete deletes a share transfer. +func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := client.Delete(deleteURL(client, id), &gophercloud.RequestOpts{ + // DELETE requests response with a 200 code, adding it here + OkCodes: []int{200, 202, 204}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// ListOptsBuilder allows extensions to add additional parameters to the List +// request. +type ListOptsBuilder interface { + ToTransferListQuery() (string, error) +} + +// ListOpts holds options for listing Transfers. It is passed to the sharetransfers.List +// or sharetransfers.ListDetail functions. +type ListOpts struct { + // AllTenants will retrieve transfers of all tenants/projects. Admin + // only. + AllTenants bool `q:"all_tenants"` + + // The user defined name of the share transfer to filter resources by. + Name string `q:"name"` + + // The name pattern that can be used to filter share transfers. + NamePattern string `q:"name~"` + + // The key to sort a list of transfers. A valid value is id, name, + // resource_type, resource_id, source_project_id, destination_project_id, + // created_at, expires_at. + SortKey string `q:"sort_key"` + + // The direction to sort a list of resources. A valid value is asc, or + // desc. + SortDir string `q:"sort_dir"` + + // 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"` +} + +// ToTransferListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToTransferListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// List returns Transfers 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.ToTransferListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + p := TransferPage{pagination.MarkerPageBase{PageResult: r}} + p.MarkerPageBase.Owner = p + return p + }) +} + +// List returns Transfers with details optionally limited by the conditions +// provided in ListOpts. +func ListDetail(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := listDetailURL(client) + if opts != nil { + query, err := opts.ToTransferListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + p := TransferPage{pagination.MarkerPageBase{PageResult: r}} + p.MarkerPageBase.Owner = p + return p + }) +} + +// Get retrieves the Transfer with the provided ID. To extract the Transfer 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 +} diff --git a/openstack/sharedfilesystems/v2/sharetransfers/results.go b/openstack/sharedfilesystems/v2/sharetransfers/results.go new file mode 100644 index 0000000000..bc91e3a165 --- /dev/null +++ b/openstack/sharedfilesystems/v2/sharetransfers/results.go @@ -0,0 +1,170 @@ +package sharetransfers + +import ( + "encoding/json" + "net/url" + "strconv" + "time" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +const ( + invalidMarker = "-1" +) + +// Transfer represents a Share Transfer record. +type Transfer struct { + ID string `json:"id"` + Accepted bool `json:"accepted"` + AuthKey string `json:"auth_key"` + Name string `json:"name"` + SourceProjectID string `json:"source_project_id"` + DestinationProjectID string `json:"destination_project_id"` + ResourceID string `json:"resource_id"` + ResourceType string `json:"resource_type"` + CreatedAt time.Time `json:"-"` + ExpiresAt time.Time `json:"-"` + Links []map[string]string `json:"links"` +} + +// UnmarshalJSON is our unmarshalling helper. +func (r *Transfer) UnmarshalJSON(b []byte) error { + type tmp Transfer + var s struct { + tmp + CreatedAt gophercloud.JSONRFC3339MilliNoZ `json:"created_at"` + ExpiresAt gophercloud.JSONRFC3339MilliNoZ `json:"expires_at"` + } + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + *r = Transfer(s.tmp) + + r.CreatedAt = time.Time(s.CreatedAt) + r.ExpiresAt = time.Time(s.ExpiresAt) + + return err +} + +type commonResult struct { + gophercloud.Result +} + +// Extract will get the Transfer object out of the commonResult object. +func (r commonResult) Extract() (*Transfer, error) { + var s Transfer + err := r.ExtractInto(&s) + return &s, err +} + +// ExtractInto converts our response data into a transfer struct. +func (r commonResult) ExtractInto(v interface{}) error { + return r.Result.ExtractIntoStructPtr(v, "transfer") +} + +// CreateResult contains the response body and error from a Create request. +type CreateResult struct { + commonResult +} + +// GetResult contains the response body and error from a Get request. +type GetResult struct { + commonResult +} + +// DeleteResult contains the response body and error from a Delete request. +type DeleteResult struct { + gophercloud.ErrResult +} + +// AcceptResult contains the response body and error from an Accept request. +type AcceptResult struct { + gophercloud.ErrResult +} + +// ExtractTransfers extracts and returns Transfers. It is used while iterating over a transfers.List call. +func ExtractTransfers(r pagination.Page) ([]Transfer, error) { + var s []Transfer + err := ExtractTransfersInto(r, &s) + return s, err +} + +// ExtractTransfersInto similar to ExtractInto but operates on a `list` of transfers +func ExtractTransfersInto(r pagination.Page, v interface{}) error { + return r.(TransferPage).Result.ExtractIntoSlicePtr(v, "transfers") +} + +// TransferPage is a pagination.pager that is returned from a call to the List function. +type TransferPage struct { + pagination.MarkerPageBase +} + +// NextPageURL generates the URL for the page of results after this one. +func (r TransferPage) NextPageURL() (string, error) { + currentURL := r.URL + mark, err := r.Owner.LastMarker() + if err != nil { + return "", err + } + if mark == invalidMarker { + return "", nil + } + + q := currentURL.Query() + q.Set("offset", mark) + currentURL.RawQuery = q.Encode() + return currentURL.String(), nil +} + +// LastMarker returns the last offset in a ListResult. +func (r TransferPage) LastMarker() (string, error) { + replicas, err := ExtractTransfers(r) + if err != nil { + return invalidMarker, err + } + if len(replicas) == 0 { + return invalidMarker, nil + } + + u, err := url.Parse(r.URL.String()) + if err != nil { + return invalidMarker, err + } + queryParams := u.Query() + offset := queryParams.Get("offset") + limit := queryParams.Get("limit") + + // Limit is not present, only one page required + if limit == "" { + return invalidMarker, nil + } + + iOffset := 0 + if offset != "" { + iOffset, err = strconv.Atoi(offset) + if err != nil { + return invalidMarker, err + } + } + iLimit, err := strconv.Atoi(limit) + if err != nil { + return invalidMarker, err + } + iOffset = iOffset + iLimit + offset = strconv.Itoa(iOffset) + + return offset, nil +} + +// IsEmpty satisifies the IsEmpty method of the Page interface. +func (r TransferPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + + replicas, err := ExtractTransfers(r) + return len(replicas) == 0, err +} diff --git a/openstack/sharedfilesystems/v2/sharetransfers/testing/fixtures.go b/openstack/sharedfilesystems/v2/sharetransfers/testing/fixtures.go new file mode 100644 index 0000000000..a492d4597f --- /dev/null +++ b/openstack/sharedfilesystems/v2/sharetransfers/testing/fixtures.go @@ -0,0 +1,207 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + "time" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/sharetransfers" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +const ListOutput = ` +{ + "transfers": [ + { + "created_at": "2020-02-28T12:44:28.051989", + "resource_id": "2f6f1684-1ded-40db-8a49-7c87dedbc758", + "id": "b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", + "links": [ + { + "href": "https://share/v3/53c2b94f63fb4f43a21b92d119ce549f/share-transfers/b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", + "rel": "self" + }, + { + "href": "https://share/53c2b94f63fb4f43a21b92d119ce549f/share-transfers/b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", + "rel": "bookmark" + } + ], + "name": null + } + ] +} +` + +const GetOutput = ` +{ + "transfer": { + "created_at": "2020-02-28T12:44:28.051989", + "resource_id": "2f6f1684-1ded-40db-8a49-7c87dedbc758", + "id": "b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", + "links": [ + { + "href": "https://share/v3/53c2b94f63fb4f43a21b92d119ce549f/share-transfers/b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", + "rel": "self" + }, + { + "href": "https://share/53c2b94f63fb4f43a21b92d119ce549f/share-transfers/b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", + "rel": "bookmark" + } + ], + "name": null + } +} +` + +const CreateRequest = ` +{ + "transfer": { + "share_id": "2f6f1684-1ded-40db-8a49-7c87dedbc758" + } +} +` + +const CreateResponse = ` +{ + "transfer": { + "auth_key": "cb67e0e7387d9eac", + "created_at": "2020-02-28T12:44:28.051989", + "id": "b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", + "links": [ + { + "href": "https://share/v3/53c2b94f63fb4f43a21b92d119ce549f/share-transfers/b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", + "rel": "self" + }, + { + "href": "https://share/53c2b94f63fb4f43a21b92d119ce549f/share-transfers/b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", + "rel": "bookmark" + } + ], + "name": null, + "resource_id": "2f6f1684-1ded-40db-8a49-7c87dedbc758" + } +} +` + +const AcceptTransferRequest = ` +{ + "accept": { + "auth_key": "9266c59563c84664" + } +} +` + +var TransferRequest = sharetransfers.CreateOpts{ + ShareID: "2f6f1684-1ded-40db-8a49-7c87dedbc758", +} + +var createdAt, _ = time.Parse(gophercloud.RFC3339MilliNoZ, "2020-02-28T12:44:28.051989") +var TransferResponse = sharetransfers.Transfer{ + ID: "b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", + AuthKey: "cb67e0e7387d9eac", + Name: "", + ResourceID: "2f6f1684-1ded-40db-8a49-7c87dedbc758", + CreatedAt: createdAt, + Links: []map[string]string{ + { + "href": "https://share/v3/53c2b94f63fb4f43a21b92d119ce549f/share-transfers/b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", + "rel": "self", + }, + { + "href": "https://share/53c2b94f63fb4f43a21b92d119ce549f/share-transfers/b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", + "rel": "bookmark", + }, + }, +} + +var TransferListResponse = []sharetransfers.Transfer{TransferResponse} + +var AcceptRequest = sharetransfers.AcceptOpts{ + AuthKey: "9266c59563c84664", +} + +var AcceptResponse = sharetransfers.Transfer{ + ID: "b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", + Name: "", + ResourceID: "2f6f1684-1ded-40db-8a49-7c87dedbc758", + Links: []map[string]string{ + { + "href": "https://share/v3/53c2b94f63fb4f43a21b92d119ce549f/share-transfers/b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", + "rel": "self", + }, + { + "href": "https://share/53c2b94f63fb4f43a21b92d119ce549f/share-transfers/b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", + "rel": "bookmark", + }, + }, +} + +func HandleCreateTransfer(t *testing.T) { + th.Mux.HandleFunc("/share-transfers", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + w.Header().Add("Content-Type", "application/json") + th.TestJSONRequest(t, r, CreateRequest) + + w.WriteHeader(http.StatusAccepted) + fmt.Fprintf(w, CreateResponse) + }) +} + +func HandleAcceptTransfer(t *testing.T) { + th.Mux.HandleFunc("/share-transfers/b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f/accept", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + w.Header().Add("Content-Type", "application/json") + th.TestJSONRequest(t, r, AcceptTransferRequest) + + w.WriteHeader(http.StatusAccepted) + }) +} + +func HandleDeleteTransfer(t *testing.T) { + th.Mux.HandleFunc("/share-transfers/b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "DELETE") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + w.WriteHeader(http.StatusOK) + }) +} + +func HandleListTransfers(t *testing.T) { + th.Mux.HandleFunc("/share-transfers", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + w.Header().Add("Content-Type", "application/json") + th.TestFormValues(t, r, map[string]string{"all_tenants": "true"}) + + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, ListOutput) + }) +} + +func HandleListTransfersDetail(t *testing.T) { + th.Mux.HandleFunc("/share-transfers/detail", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + w.Header().Add("Content-Type", "application/json") + th.TestFormValues(t, r, map[string]string{"all_tenants": "true"}) + + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, ListOutput) + }) +} + +func HandleGetTransfer(t *testing.T) { + th.Mux.HandleFunc("/share-transfers/b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + w.Header().Add("Content-Type", "application/json") + + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, GetOutput) + }) +} diff --git a/openstack/sharedfilesystems/v2/sharetransfers/testing/requests_test.go b/openstack/sharedfilesystems/v2/sharetransfers/testing/requests_test.go new file mode 100644 index 0000000000..75ed482c45 --- /dev/null +++ b/openstack/sharedfilesystems/v2/sharetransfers/testing/requests_test.go @@ -0,0 +1,112 @@ +package testing + +import ( + "testing" + + "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/sharetransfers" + "github.com/gophercloud/gophercloud/pagination" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestCreateTransfer(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleCreateTransfer(t) + + actual, err := sharetransfers.Create(client.ServiceClient(), TransferRequest).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, TransferResponse, *actual) +} + +func TestAcceptTransfer(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleAcceptTransfer(t) + + err := sharetransfers.Accept(client.ServiceClient(), TransferResponse.ID, AcceptRequest).ExtractErr() + th.AssertNoErr(t, err) +} + +func TestDeleteTransfer(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleDeleteTransfer(t) + + err := sharetransfers.Delete(client.ServiceClient(), TransferResponse.ID).ExtractErr() + th.AssertNoErr(t, err) +} + +func TestListTransfers(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListTransfers(t) + + expectedResponse := TransferListResponse + expectedResponse[0].AuthKey = "" + + count := 0 + err := sharetransfers.List(client.ServiceClient(), &sharetransfers.ListOpts{AllTenants: true}).EachPage(func(page pagination.Page) (bool, error) { + count++ + + actual, err := sharetransfers.ExtractTransfers(page) + th.AssertNoErr(t, err) + + th.CheckDeepEquals(t, expectedResponse, actual) + + return true, nil + }) + th.AssertNoErr(t, err) + th.CheckEquals(t, count, 1) +} + +func TestListTransfersDetail(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListTransfersDetail(t) + + expectedResponse := TransferListResponse + expectedResponse[0].AuthKey = "" + + count := 0 + err := sharetransfers.ListDetail(client.ServiceClient(), &sharetransfers.ListOpts{AllTenants: true}).EachPage(func(page pagination.Page) (bool, error) { + count++ + + actual, err := sharetransfers.ExtractTransfers(page) + th.AssertNoErr(t, err) + + th.CheckDeepEquals(t, expectedResponse, actual) + + return true, nil + }) + th.AssertNoErr(t, err) + th.CheckEquals(t, count, 1) +} + +func TestListTransfersAllPages(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListTransfers(t) + + expectedResponse := TransferListResponse + expectedResponse[0].AuthKey = "" + + allPages, err := sharetransfers.List(client.ServiceClient(), &sharetransfers.ListOpts{AllTenants: true}).AllPages() + th.AssertNoErr(t, err) + actual, err := sharetransfers.ExtractTransfers(allPages) + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, expectedResponse, actual) +} + +func TestGetTransfer(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleGetTransfer(t) + + expectedResponse := TransferResponse + expectedResponse.AuthKey = "" + + actual, err := sharetransfers.Get(client.ServiceClient(), TransferResponse.ID).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, expectedResponse, *actual) +} diff --git a/openstack/sharedfilesystems/v2/sharetransfers/urls.go b/openstack/sharedfilesystems/v2/sharetransfers/urls.go new file mode 100644 index 0000000000..1513f38cc9 --- /dev/null +++ b/openstack/sharedfilesystems/v2/sharetransfers/urls.go @@ -0,0 +1,27 @@ +package sharetransfers + +import "github.com/gophercloud/gophercloud" + +func transferURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("share-transfers") +} + +func acceptURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("share-transfers", id, "accept") +} + +func deleteURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("share-transfers", id) +} + +func listURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("share-transfers") +} + +func listDetailURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("share-transfers", "detail") +} + +func getURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("share-transfers", id) +} From 19c426da4d7350d40cc9786098291774e57d3aca Mon Sep 17 00:00:00 2001 From: Pierre Prinetti <pierreprinetti@redhat.com> Date: Tue, 20 Jun 2023 15:18:21 +0200 Subject: [PATCH 265/360] Prepare release v1.5.0 --- CHANGELOG.md | 11 +++++++++++ provider_client.go | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e737082d69..e19d5af517 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,14 @@ +## 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: diff --git a/provider_client.go b/provider_client.go index 12273d8049..6cfb14fd7e 100644 --- a/provider_client.go +++ b/provider_client.go @@ -14,7 +14,7 @@ import ( // DefaultUserAgent is the default User-Agent string set in the request header. const ( - DefaultUserAgent = "gophercloud/v1.4.0" + DefaultUserAgent = "gophercloud/v1.5.0" DefaultMaxBackoffRetries = 60 ) From a0b5dd46def95a1114f63af69174d6da60c2cf4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= <m.andre@redhat.com> Date: Thu, 6 Jul 2023 10:45:32 +0900 Subject: [PATCH 266/360] [v1][CI] Drop periodic jobs from stable branch Periodic jobs can't run against non-default branches with github action. Remove them from our stable branches to avoid confusion. According to the github documentation [1]: Scheduled workflows run on the latest commit on the default or base branch. [1] https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#schedule --- .github/workflows/codeql-analysis.yml | 2 -- .github/workflows/functional-baremetal.yaml | 2 -- .github/workflows/functional-basic.yaml | 2 -- .github/workflows/functional-blockstorage.yaml | 2 -- .github/workflows/functional-clustering.yaml | 2 -- .github/workflows/functional-compute.yaml | 2 -- .github/workflows/functional-containerinfra.yaml | 2 -- .github/workflows/functional-dns.yaml | 2 -- .github/workflows/functional-fwaas_v2.yaml | 2 -- .github/workflows/functional-identity.yaml | 2 -- .github/workflows/functional-imageservice.yaml | 2 -- .github/workflows/functional-keymanager.yaml | 2 -- .github/workflows/functional-loadbalancer.yaml | 2 -- .github/workflows/functional-messaging.yaml | 2 -- .github/workflows/functional-networking.yaml | 2 -- .github/workflows/functional-objectstorage.yaml | 2 -- .github/workflows/functional-orchestration.yaml | 2 -- .github/workflows/functional-placement.yaml | 2 -- .github/workflows/functional-sharedfilesystems.yaml | 2 -- 19 files changed, 38 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index db762de4d7..3832784cb4 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -6,8 +6,6 @@ on: pull_request: # The branches below must be a subset of the branches above branches: [ master ] - schedule: - - cron: '18 8 * * 6' jobs: analyze: diff --git a/.github/workflows/functional-baremetal.yaml b/.github/workflows/functional-baremetal.yaml index 0ae2f53a29..277245d1c6 100644 --- a/.github/workflows/functional-baremetal.yaml +++ b/.github/workflows/functional-baremetal.yaml @@ -3,8 +3,6 @@ on: pull_request: paths: - '**baremetal**' - schedule: - - cron: '0 0 */3 * *' jobs: functional-baremetal: strategy: diff --git a/.github/workflows/functional-basic.yaml b/.github/workflows/functional-basic.yaml index 727fb9d1e0..982e006151 100644 --- a/.github/workflows/functional-basic.yaml +++ b/.github/workflows/functional-basic.yaml @@ -6,8 +6,6 @@ on: - '**.md' - '**.gitignore' - '**LICENSE' - schedule: - - cron: '0 0 */3 * *' jobs: functional-basic: strategy: diff --git a/.github/workflows/functional-blockstorage.yaml b/.github/workflows/functional-blockstorage.yaml index 07624e7cb1..4dfd04de59 100644 --- a/.github/workflows/functional-blockstorage.yaml +++ b/.github/workflows/functional-blockstorage.yaml @@ -3,8 +3,6 @@ on: pull_request: paths: - '**blockstorage**' - schedule: - - cron: '0 0 */3 * *' jobs: functional-blockstorage: strategy: diff --git a/.github/workflows/functional-clustering.yaml b/.github/workflows/functional-clustering.yaml index 110920487b..fa9a8a5a46 100644 --- a/.github/workflows/functional-clustering.yaml +++ b/.github/workflows/functional-clustering.yaml @@ -3,8 +3,6 @@ on: pull_request: paths: - '**clustering**' - schedule: - - cron: '0 0 */3 * *' jobs: functional-clustering: strategy: diff --git a/.github/workflows/functional-compute.yaml b/.github/workflows/functional-compute.yaml index 188064e4dc..99448f7e48 100644 --- a/.github/workflows/functional-compute.yaml +++ b/.github/workflows/functional-compute.yaml @@ -3,8 +3,6 @@ on: pull_request: paths: - '**compute**' - schedule: - - cron: '0 0 */3 * *' jobs: functional-compute: strategy: diff --git a/.github/workflows/functional-containerinfra.yaml b/.github/workflows/functional-containerinfra.yaml index 4f1aca8db3..9dbd8dc5da 100644 --- a/.github/workflows/functional-containerinfra.yaml +++ b/.github/workflows/functional-containerinfra.yaml @@ -3,8 +3,6 @@ on: pull_request: paths: - '**containerinfra**' - schedule: - - cron: '0 0 */3 * *' jobs: functional-containerinfra: strategy: diff --git a/.github/workflows/functional-dns.yaml b/.github/workflows/functional-dns.yaml index ec9b829d96..a14e0fbf02 100644 --- a/.github/workflows/functional-dns.yaml +++ b/.github/workflows/functional-dns.yaml @@ -4,8 +4,6 @@ on: paths: - '**openstack/dns**' - '**functional-dns.yaml' - schedule: - - cron: '0 0 */3 * *' jobs: functional-dns: strategy: diff --git a/.github/workflows/functional-fwaas_v2.yaml b/.github/workflows/functional-fwaas_v2.yaml index 4c0b4463ee..ecd53ef5be 100644 --- a/.github/workflows/functional-fwaas_v2.yaml +++ b/.github/workflows/functional-fwaas_v2.yaml @@ -3,8 +3,6 @@ on: pull_request: paths: - '**networking/extensions/fwaas_v2**' - schedule: - - cron: '0 0 */3 * *' jobs: functional-fwaas_v2: strategy: diff --git a/.github/workflows/functional-identity.yaml b/.github/workflows/functional-identity.yaml index 9cf1a44b8b..df0764beb0 100644 --- a/.github/workflows/functional-identity.yaml +++ b/.github/workflows/functional-identity.yaml @@ -3,8 +3,6 @@ on: pull_request: paths: - '**identity**' - schedule: - - cron: '0 0 */3 * *' jobs: functional-identity: strategy: diff --git a/.github/workflows/functional-imageservice.yaml b/.github/workflows/functional-imageservice.yaml index 7b1c3fefd9..77ee23bad4 100644 --- a/.github/workflows/functional-imageservice.yaml +++ b/.github/workflows/functional-imageservice.yaml @@ -3,8 +3,6 @@ on: pull_request: paths: - '**imageservice**' - schedule: - - cron: '0 0 */3 * *' jobs: functional-imageservice: strategy: diff --git a/.github/workflows/functional-keymanager.yaml b/.github/workflows/functional-keymanager.yaml index cd270e7416..42f097c655 100644 --- a/.github/workflows/functional-keymanager.yaml +++ b/.github/workflows/functional-keymanager.yaml @@ -3,8 +3,6 @@ on: pull_request: paths: - '**keymanager**' - schedule: - - cron: '0 0 */3 * *' jobs: functional-keymanager: strategy: diff --git a/.github/workflows/functional-loadbalancer.yaml b/.github/workflows/functional-loadbalancer.yaml index 843f3645c5..0b8cc40f6b 100644 --- a/.github/workflows/functional-loadbalancer.yaml +++ b/.github/workflows/functional-loadbalancer.yaml @@ -3,8 +3,6 @@ on: pull_request: paths: - '**loadbalancer**' - schedule: - - cron: '0 0 */3 * *' jobs: functional-loadbalancer: strategy: diff --git a/.github/workflows/functional-messaging.yaml b/.github/workflows/functional-messaging.yaml index b727eafbce..8314a1b496 100644 --- a/.github/workflows/functional-messaging.yaml +++ b/.github/workflows/functional-messaging.yaml @@ -3,8 +3,6 @@ on: pull_request: paths: - '**messaging**' - schedule: - - cron: '0 0 */3 * *' jobs: functional-messaging: strategy: diff --git a/.github/workflows/functional-networking.yaml b/.github/workflows/functional-networking.yaml index 3f69e5abbe..feca87453b 100644 --- a/.github/workflows/functional-networking.yaml +++ b/.github/workflows/functional-networking.yaml @@ -3,8 +3,6 @@ on: pull_request: paths: - '**networking**' - schedule: - - cron: '0 0 */3 * *' jobs: functional-networking: strategy: diff --git a/.github/workflows/functional-objectstorage.yaml b/.github/workflows/functional-objectstorage.yaml index f43f6cad8a..125a7add35 100644 --- a/.github/workflows/functional-objectstorage.yaml +++ b/.github/workflows/functional-objectstorage.yaml @@ -3,8 +3,6 @@ on: pull_request: paths: - '**objectstorage**' - schedule: - - cron: '0 0 */3 * *' jobs: functional-objectstorage: strategy: diff --git a/.github/workflows/functional-orchestration.yaml b/.github/workflows/functional-orchestration.yaml index 12dbe82a63..90391c4520 100644 --- a/.github/workflows/functional-orchestration.yaml +++ b/.github/workflows/functional-orchestration.yaml @@ -3,8 +3,6 @@ on: pull_request: paths: - '**orchestration**' - schedule: - - cron: '0 0 */3 * *' jobs: functional-orchestration: strategy: diff --git a/.github/workflows/functional-placement.yaml b/.github/workflows/functional-placement.yaml index 57d5990b0a..71b24efcce 100644 --- a/.github/workflows/functional-placement.yaml +++ b/.github/workflows/functional-placement.yaml @@ -3,8 +3,6 @@ on: pull_request: paths: - '**placement**' - schedule: - - cron: '0 0 */3 * *' jobs: functional-placement: strategy: diff --git a/.github/workflows/functional-sharedfilesystems.yaml b/.github/workflows/functional-sharedfilesystems.yaml index b72c33e75c..7d552435ef 100644 --- a/.github/workflows/functional-sharedfilesystems.yaml +++ b/.github/workflows/functional-sharedfilesystems.yaml @@ -3,8 +3,6 @@ on: pull_request: paths: - '**sharedfilesystems**' - schedule: - - cron: '0 0 */3 * *' jobs: functional-sharedfilesystems: strategy: From 0a5bb2b306e046f4035b6d311a2b43036815902c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= <m.andre@redhat.com> Date: Mon, 10 Jul 2023 09:53:55 +0900 Subject: [PATCH 267/360] [v1] Remove the Ensure labels workflow These labels are synced from the master branch. --- .github/semver-labels.yaml | 9 --------- .github/workflows/semver-labels.yaml | 18 ------------------ 2 files changed, 27 deletions(-) delete mode 100644 .github/semver-labels.yaml delete mode 100644 .github/workflows/semver-labels.yaml diff --git a/.github/semver-labels.yaml b/.github/semver-labels.yaml deleted file mode 100644 index 7e9c8811b5..0000000000 --- a/.github/semver-labels.yaml +++ /dev/null @@ -1,9 +0,0 @@ -- name: semver:major - description: Breaking change - color: '9E1957' -- name: semver:minor - description: Backwards-compatible change - color: 'FBCA04' -- name: semver:patch - description: No API change - color: '6E7624' diff --git a/.github/workflows/semver-labels.yaml b/.github/workflows/semver-labels.yaml deleted file mode 100644 index ccaf44522b..0000000000 --- a/.github/workflows/semver-labels.yaml +++ /dev/null @@ -1,18 +0,0 @@ -name: Ensure labels -on: - push: - branches: - - master - paths: - - .github/semver-labels.yaml - - .github/workflows/semver-labels.yaml -jobs: - semver: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: micnncim/action-label-syncer@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - manifest: .github/semver-labels.yaml From 457c1cfe72af78492f9004e22954fc57394959e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= <m.andre@redhat.com> Date: Mon, 10 Jul 2023 09:56:56 +0900 Subject: [PATCH 268/360] [v1] Allow running 'CodeQL' job for non-master branches Drop the branch filter that was in place to allow running the `CodeQL` job from any branch. Partial backport of 9a407d6. --- .github/workflows/codeql-analysis.yml | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 3832784cb4..38cf2bfb08 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -1,11 +1,6 @@ name: "CodeQL" -on: - push: - branches: [ master ] - pull_request: - # The branches below must be a subset of the branches above - branches: [ master ] +on: [push, pull_request] jobs: analyze: From 2d1a32f709c97a50706b74a7dc4739a8a9514d7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= <m.andre@redhat.com> Date: Mon, 10 Jul 2023 10:05:48 +0900 Subject: [PATCH 269/360] [v1] Update Readme Remove obsolete link to travis-ci, and update branch for coveralls.io badge. --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 89b08156fe..e2520361de 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,5 @@ # Gophercloud: an OpenStack SDK for Go -[](https://travis-ci.org/gophercloud/gophercloud) -[](https://coveralls.io/github/gophercloud/gophercloud?branch=master) +[](https://coveralls.io/github/gophercloud/gophercloud?branch=v1) Gophercloud is an OpenStack Go SDK. From 73dd87c5fc6441dda2f3292a18fe912ef023c734 Mon Sep 17 00:00:00 2001 From: Emilien Macchi <emacchi@redhat.com> Date: Tue, 1 Aug 2023 11:21:54 -0400 Subject: [PATCH 270/360] README: minor change to test backport workflow --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e2520361de..4e6e57dadb 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ You will need to retrieve the following: Credentials, a pre-generated token, or any other supported authentication mechanism. -For users that have the OpenStack dashboard installed, there's a shortcut. If +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 From b1d38dd1cc7b069dad1ab2bb40e385471c2a2d52 Mon Sep 17 00:00:00 2001 From: Emilien Macchi <emacchi@redhat.com> Date: Wed, 28 Jun 2023 15:31:34 -0400 Subject: [PATCH 271/360] tests: run MultiAttach with a capable Cinder Type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Starting from the Queens release, it is possible to attach a volume to multiple hosts/servers, using a volume type with multiattach capability. Cinder previously had a `multiattach` parameter that could be passed during the volume creation. This parameter was recently removed [1], and caused our jobs to fail. This commit change the tests to no longer use the removed parameter. Fixes #2657. Co-Authored-By: Martin André <m.andre@redhat.com> [1] https://github.com/openstack/cinder/commit/d4535c77493a7b362091b962f42f2613dea65dbe --- .../openstack/blockstorage/v3/blockstorage.go | 32 ++++++++++++++++++- .../openstack/blockstorage/v3/volumes_test.go | 11 ++++--- 2 files changed, 38 insertions(+), 5 deletions(-) diff --git a/acceptance/openstack/blockstorage/v3/blockstorage.go b/acceptance/openstack/blockstorage/v3/blockstorage.go index 5e05024a1c..1645f327b6 100644 --- a/acceptance/openstack/blockstorage/v3/blockstorage.go +++ b/acceptance/openstack/blockstorage/v3/blockstorage.go @@ -177,6 +177,36 @@ func CreateVolumeTypeNoExtraSpecs(t *testing.T, client *gophercloud.ServiceClien return vt, nil } +// CreateVolumeTypeMultiAttach will create a volume type with a random name and +// extra specs for multi-attach. An error will be returned if the volume type was +// unable to be created. +func CreateVolumeTypeMultiAttach(t *testing.T, client *gophercloud.ServiceClient) (*volumetypes.VolumeType, error) { + name := tools.RandomString("ACPTTEST", 16) + description := "create_from_gophercloud" + t.Logf("Attempting to create volume type: %s", name) + + createOpts := volumetypes.CreateOpts{ + Name: name, + ExtraSpecs: map[string]string{"multiattach": "<is> True"}, + Description: description, + } + + vt, err := volumetypes.Create(client, createOpts).Extract() + if err != nil { + return nil, err + } + + tools.PrintResource(t, vt) + th.AssertEquals(t, vt.IsPublic, true) + th.AssertEquals(t, vt.Name, name) + th.AssertEquals(t, vt.Description, description) + th.AssertEquals(t, vt.ExtraSpecs["multiattach"], "<is> True") + + t.Logf("Successfully created volume type: %s", vt.ID) + + return vt, nil +} + // CreatePrivateVolumeType will create a private volume type with a random // name and no extra specs. An error will be returned if the volume type was // unable to be created. @@ -268,7 +298,7 @@ func DeleteVolumeType(t *testing.T, client *gophercloud.ServiceClient, vt *volum err := volumetypes.Delete(client, vt.ID).ExtractErr() if err != nil { - t.Fatalf("Unable to delete volume %s: %v", vt.ID, err) + t.Fatalf("Unable to delete volume type %s: %v", vt.ID, err) } t.Logf("Successfully deleted volume type: %s", vt.ID) diff --git a/acceptance/openstack/blockstorage/v3/volumes_test.go b/acceptance/openstack/blockstorage/v3/volumes_test.go index 186b32f039..b86f70863a 100644 --- a/acceptance/openstack/blockstorage/v3/volumes_test.go +++ b/acceptance/openstack/blockstorage/v3/volumes_test.go @@ -67,30 +67,33 @@ func TestVolumes(t *testing.T) { } func TestVolumesMultiAttach(t *testing.T) { + clients.RequireAdmin(t) clients.RequireLong(t) client, err := clients.NewBlockStorageV3Client() th.AssertNoErr(t, err) + vt, err := CreateVolumeTypeMultiAttach(t, client) + th.AssertNoErr(t, err) + defer DeleteVolumeType(t, client, vt) + volumeName := tools.RandomString("ACPTTEST", 16) volOpts := volumes.CreateOpts{ Size: 1, Name: volumeName, Description: "Testing creation of multiattach enabled volume", - Multiattach: true, + VolumeType: vt.ID, } vol, err := volumes.Create(client, volOpts).Extract() th.AssertNoErr(t, err) + defer DeleteVolume(t, client, vol) err = volumes.WaitForStatus(client, vol.ID, "available", 60) th.AssertNoErr(t, err) th.AssertEquals(t, vol.Multiattach, true) - - err = volumes.Delete(client, vol.ID, volumes.DeleteOpts{}).ExtractErr() - th.AssertNoErr(t, err) } func TestVolumesCascadeDelete(t *testing.T) { From 7ac6befb2db699aa48e60c1a44a19be2c831813c Mon Sep 17 00:00:00 2001 From: georgeb <george.bampilis@surf.nl> Date: Wed, 7 Jun 2023 17:12:16 +0200 Subject: [PATCH 272/360] Add CRUD support for encryption in volume v3 types Add Create/Delete/Update/Get support for encryption of volume types --- .../blockstorage/v3/volumetypes_test.go | 42 +++++++ openstack/blockstorage/v3/volumetypes/doc.go | 58 +++++++++ .../blockstorage/v3/volumetypes/requests.go | 105 ++++++++++++++++ .../blockstorage/v3/volumetypes/results.go | 99 +++++++++++++++ .../v3/volumetypes/testing/fixtures.go | 117 ++++++++++++++++++ .../v3/volumetypes/testing/requests_test.go | 96 ++++++++++++++ openstack/blockstorage/v3/volumetypes/urls.go | 20 +++ 7 files changed, 537 insertions(+) diff --git a/acceptance/openstack/blockstorage/v3/volumetypes_test.go b/acceptance/openstack/blockstorage/v3/volumetypes_test.go index b640a63f55..73fa32722a 100644 --- a/acceptance/openstack/blockstorage/v3/volumetypes_test.go +++ b/acceptance/openstack/blockstorage/v3/volumetypes_test.go @@ -163,3 +163,45 @@ func TestVolumeTypesAccess(t *testing.T) { th.AssertEquals(t, len(accessList), 0) } + +func TestEncryptionVolumeTypes(t *testing.T) { + clients.RequireAdmin(t) + + client, err := clients.NewBlockStorageV3Client() + th.AssertNoErr(t, err) + + vt, err := CreateVolumeType(t, client) + th.AssertNoErr(t, err) + defer DeleteVolumeType(t, client, vt) + + createEncryptionOpts := volumetypes.EncryptionOptions{ + KeySize: 256, + Provider: "luks", + ControlLocation: "front-end", + Cipher: "aes-xts-plain64", + } + + eVT, err := volumetypes.CreateEncryption(client, vt.ID, createEncryptionOpts) + th.AssertNoErr(t, err) + defer DeleteEncryption(t, client, evt) + + geVT, err := volumetypes.GetEncryption(client, vt.ID).Extract() + th.AssertNoErr(t, err) + tools.PrintResource(t, geVT) + + key := "cipher" + gesVT, err := volumetypes.GetEncryptionSpec(client, vt.ID, key).Extract() + th.AssertNoErr(t, err) + tools.PrintResource(t, gesVT) + + + updateEncryptionOpts := volumetypes.EncryptionUpdateOpts{ + ControlLocation: "back-end", + } + + newEVT, err := volumetypes.Update(client, vt.ID, updateEncryptionOpts).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, newEVT) + th.AssertEquals(t, "back-end", newEVT.ControlLocation) +} diff --git a/openstack/blockstorage/v3/volumetypes/doc.go b/openstack/blockstorage/v3/volumetypes/doc.go index 55a2170bc2..d532e7262a 100644 --- a/openstack/blockstorage/v3/volumetypes/doc.go +++ b/openstack/blockstorage/v3/volumetypes/doc.go @@ -160,5 +160,63 @@ Example to Remove/Revoke Access to a Volume Type if err != nil { panic(err) } + +Example to Create the Encryption of a Volume Type + typeID := "7ffaca22-f646-41d4-b79d-d7e4452ef8cc" + volumeType, err := volumetypes.CreateEncryption(client, typeID, .CreateEncryptionOpts{ + KeySize: 256, + Provider: "luks", + ControlLocation: "front-end", + Cipher: "aes-xts-plain64", + }).Extract() + if err != nil{ + panic(err) + } + fmt.Println(volumeType) + +Example to Delete the Encryption of a Volume Type + + typeID := "7ffaca22-f646-41d4-b79d-d7e4452ef8cc" + encryptionID := ""81e069c6-7394-4856-8df7-3b237ca61f74 + err := volumetypes.DeleteEncryption(client, typeID, encryptionID).ExtractErr() + if err != nil{ + panic(err) + } + +Example to Update the Encryption of a Volume Type + + typeID := "7ffaca22-f646-41d4-b79d-d7e4452ef8cc" + volumetype, err = volumetypes.UpdateEncryption(client, typeID, volumetypes.UpdateEncryptionOpts{ + KeySize: 256, + Provider: "luks", + ControlLocation: "front-end", + Cipher: "aes-xts-plain64", + }).Extract() + if err != nil{ + panic(err) + } + fmt.Println(volumetype) + +Example to Show an Encryption of a Volume Type + + typeID := "7ffaca22-f646-41d4-b79d-d7e4452ef8cc" + volumeType, err := volumetypes.GetEncrytpion(client, typeID).Extract() + if err != nil{ + panic(err) + } + fmt.Println(volumeType) + +Example to Show an Encryption Spec of a Volume Type + + typeID := "7ffaca22-f646-41d4-b79d-d7e4452ef8cc" + key := "cipher" + volumeType, err := volumetypes.GetEncrytpionSpec(client, typeID).Extract() + if err != nil{ + panic(err) + } + fmt.Println(volumeType) + + + */ package volumetypes diff --git a/openstack/blockstorage/v3/volumetypes/requests.go b/openstack/blockstorage/v3/volumetypes/requests.go index 5b272bf05b..4b7e561bc2 100644 --- a/openstack/blockstorage/v3/volumetypes/requests.go +++ b/openstack/blockstorage/v3/volumetypes/requests.go @@ -304,3 +304,108 @@ func RemoveAccess(client *gophercloud.ServiceClient, id string, opts RemoveAcces _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } + +// CreateEncryptionOptsBuilder allows extensions to add additional parameters to the +// Create Encryption request. +type CreateEncryptionOptsBuilder interface { + ToEncryptionCreateMap() (map[string]interface{}, error) +} + +// CreateEncryptionOpts contains options for creating an Encryption Type object. +// This object is passed to the volumetypes.CreateEncryption function. +// For more information about these parameters,see the Encryption Type object. +type CreateEncryptionOpts struct { + // The size of the encryption key. + KeySize int `json:"key_size"` + // The class of that provides the encryption support. + Provider string `json:"provider" required:"true"` + // Notional service where encryption is performed. + ControlLocation string `json:"control_location"` + // The encryption algorithm or mode. + Cipher string `json:"cipher"` +} + +// ToEncryptionCreateMap assembles a request body based on the contents of a +// CreateEncryptionOpts. +func (opts CreateEncryptionOpts) ToEncryptionCreateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "encryption") +} + +// CreateEncryption will creates an Encryption Type object based on the CreateEncryptionOpts. +// To extract the Encryption Type object from the response, call the Extract method on the +// EncryptionCreateResult. +func CreateEncryption(client *gophercloud.ServiceClient, id string, opts CreateEncryptionOptsBuilder) (r CreateEncryptionResult) { + b, err := opts.ToEncryptionCreateMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Post(createEncryptionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// Delete will delete an encryption type for an existing Volume Type with the provided ID. +func DeleteEncryption(client *gophercloud.ServiceClient, id, encryptionID string) (r DeleteEncryptionResult) { + resp, err := client.Delete(deleteEncryptionURL(client, id, encryptionID), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// GetEncryption retrieves the encryption type for an existing VolumeType with the provided ID. +func GetEncryption(client *gophercloud.ServiceClient, id string) (r GetEncryptionResult){ + resp, err := client.Get(getEncryptionURL(client, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// GetEncryptionSpecs retrieves the encryption type specs for an existing VolumeType with the provided ID. +func GetEncryptionSpec(client *gophercloud.ServiceClient, id, key string) (r GetEncryptionSpecResult) { + resp, err := client.Get(getEncryptionSpecURL(client, id, key), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// UpdateEncryptionOptsBuilder allows extensions to add additional parameters to the +// Update encryption request. +type UpdateEncryptionOptsBuilder interface { + ToUpdateEncryptionMap() (map[string]interface{}, error) +} + +// Update Encryption Opts contains options for creating an Update Encryption Type. This object is passed to +// the volumetypes.UpdateEncryption function. For more information about these parameters, +// see the Update Encryption Type object. +type UpdateEncryptionOpts struct { + // The size of the encryption key. + KeySize int `json:"key_size"` + // The class of that provides the encryption support. + Provider string `json:"provider"` + // Notional service where encryption is performed. + ControlLocation string `json:"control_location"` + // The encryption algorithm or mode. + Cipher string `json:"cipher"` +} + +// ToEncryptionCreateMap assembles a request body based on the contents of a +// UpdateEncryptionOpts. +func (opts UpdateEncryptionOpts) ToUpdateEncryptionMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "encryption") +} + +// Update will update an existing encryption for a Volume Type based on the values in UpdateEncryptionOpts. +// To extract the UpdateEncryption Type object from the response, call the Extract method on the +// UpdateEncryptionResult. +func UpdateEncryption(client *gophercloud.ServiceClient, id,encryptionID string, opts UpdateEncryptionOptsBuilder) (r UpdateEncryptionResult) { + b, err := opts.ToUpdateEncryptionMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Put(updateEncryptionURL(client, id, encryptionID), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} diff --git a/openstack/blockstorage/v3/volumetypes/results.go b/openstack/blockstorage/v3/volumetypes/results.go index 916b476da5..e4ff961ace 100644 --- a/openstack/blockstorage/v3/volumetypes/results.go +++ b/openstack/blockstorage/v3/volumetypes/results.go @@ -200,3 +200,102 @@ type AddAccessResult struct { type RemoveAccessResult struct { gophercloud.ErrResult } + + +type EncryptionType struct { + // Unique identifier for the volume type. + VolumeTypeID string `json:"volume_type_id"` + // Notional service where encryption is performed. + ControlLocation string `json:"control_location"` + // Unique identifier for encryption type. + EncryptionID string `json:"encryption_id"` + // Size of encryption key. + KeySize int `json:"key_size"` + // Class that provides encryption support. + Provider string `json:"provider"` + // The encryption algorithm or mode. + Cipher string `json:"cipher"` +} + +type encryptionResult struct { + gophercloud.Result +} + +func (r encryptionResult) Extract() (*EncryptionType, error) { + var s EncryptionType + err := r.ExtractInto(&s) + return &s, err +} + +// ExtractInto converts our response data into a volume type struct +func (r encryptionResult) ExtractInto(v interface{}) error { + return r.Result.ExtractIntoStructPtr(v, "encryption") +} + +type CreateEncryptionResult struct { + encryptionResult +} + +// UpdateResult contains the response body and error from an UpdateEncryption request. +type UpdateEncryptionResult struct { + encryptionResult +} + +// DeleteEncryptionResult contains the response body and error from a DeleteEncryprion request. +type DeleteEncryptionResult struct { + gophercloud.ErrResult +} + +type GetEncryptionType struct { + // Unique identifier for the volume type. + VolumeTypeID string `json:"volume_type_id"` + // Notional service where encryption is performed. + ControlLocation string `json:"control_location"` + // Shows if the resource is deleted or Notional + Deleted bool `json:"deleted"` + // Shows the date and time the resource was created. + CreatedAt string `json:"created_at"` + // Shows the date and time when resource was updated. + UpdatedAt string `json:"updated_at"` + // Unique identifier for encryption type. + EncryptionID string `json:"encryption_id"` + // Size of encryption key. + KeySize int `json:"key_size"` + // Class that provides encryption support. + Provider string `json:"provider"` + // Shows the date and time the reousrce was deleted. + DeletedAt string `json:"deleted_at"` + // The encryption algorithm or mode. + Cipher string `json:"cipher"` +} + +type encryptionShowResult struct { + gophercloud.Result +} + +// Extract interprets any extraSpecResult as an ExtraSpec, if possible. +func (r encryptionShowResult) Extract() (* GetEncryptionType, error) { + var s GetEncryptionType + err := r.ExtractInto(&s) + return &s, err +} + +type GetEncryptionResult struct { + encryptionShowResult +} + +type encryptionShowSpecResult struct{ + gophercloud.Result +} + +// Extract interprets any empty interface Result as an empty interface. +func (r encryptionShowSpecResult) Extract() (map[string]interface{}, error) { + var s map[string]interface{} + err := r.ExtractInto(&s) + return s, err +} + +type GetEncryptionSpecResult struct{ + encryptionShowSpecResult +} + diff --git a/openstack/blockstorage/v3/volumetypes/testing/fixtures.go b/openstack/blockstorage/v3/volumetypes/testing/fixtures.go index eb617f19e5..bee9cee1e0 100644 --- a/openstack/blockstorage/v3/volumetypes/testing/fixtures.go +++ b/openstack/blockstorage/v3/volumetypes/testing/fixtures.go @@ -258,3 +258,120 @@ func HandleExtraSpecDeleteSuccessfully(t *testing.T) { w.WriteHeader(http.StatusAccepted) }) } + +func MockEncryptionCreateResponse(t *testing.T) { + th.Mux.HandleFunc("/types/a5082c24-2a27-43a4-b48e-fcec1240e36b/encryption", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestHeader(t, r, "Content-Type", "application/json") + th.TestHeader(t, r, "Accept", "application/json") + th.TestJSONRequest(t, r, ` +{ + "encryption": { + "key_size": 256, + "provider": "luks", + "control_location": "front-end", + "cipher": "aes-xts-plain64" + } +} + `) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, ` +{ + "encryption": { + "volume_type_id": "a5082c24-2a27-43a4-b48e-fcec1240e36b", + "control_location": "front-end", + "encryption_id": "81e069c6-7394-4856-8df7-3b237ca61f74", + "key_size": 256, + "provider": "luks", + "cipher": "aes-xts-plain64" + } +} + `) + }) +} + +func MockDeleteEncryptionResponse(t *testing.T) { + th.Mux.HandleFunc("/types/a5082c24-2a27-43a4-b48e-fcec1240e36b/encryption/81e069c6-7394-4856-8df7-3b237ca61f74", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "DELETE") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + w.WriteHeader(http.StatusAccepted) + }) +} + +func MockEncryptionUpdateResponse(t *testing.T) { + th.Mux.HandleFunc("/types/a5082c24-2a27-43a4-b48e-fcec1240e36b/encryption/81e069c6-7394-4856-8df7-3b237ca61f74", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PUT") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestHeader(t, r, "Content-Type", "application/json") + th.TestHeader(t, r, "Accept", "application/json") + th.TestJSONRequest(t, r, ` +{ + "encryption": { + "key_size": 256, + "provider": "luks", + "control_location": "front-end", + "cipher": "aes-xts-plain64" + } +} + `) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, ` +{ + "encryption": { + "control_location": "front-end", + "key_size": 256, + "provider": "luks", + "cipher": "aes-xts-plain64" + } +} + `) + }) +} + +func MockEncryptionGetResponse(t *testing.T) { + th.Mux.HandleFunc("/types/a5082c24-2a27-43a4-b48e-fcec1240e36b/encryption", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, ` +{ + "volume_type_id": "a5082c24-2a27-43a4-b48e-fcec1240e36b", + "control_location": "front-end", + "deleted": false, + "created_at": "2016-12-28T02:32:25.000000", + "updated_at": null, + "encryption_id": "81e069c6-7394-4856-8df7-3b237ca61f74", + "key_size": 256, + "provider": "luks", + "deleted_at": null, + "cipher": "aes-xts-plain64" +} + `) + }) +} + +func MockEncryptionGetSpecResponse(t *testing.T) { + th.Mux.HandleFunc("/types/a5082c24-2a27-43a4-b48e-fcec1240e36b/encryption/cipher", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, ` +{ + "cipher": "aes-xts-plain64" +} + `) + }) +} diff --git a/openstack/blockstorage/v3/volumetypes/testing/requests_test.go b/openstack/blockstorage/v3/volumetypes/testing/requests_test.go index eb6f2e7c0e..f3d6098490 100644 --- a/openstack/blockstorage/v3/volumetypes/testing/requests_test.go +++ b/openstack/blockstorage/v3/volumetypes/testing/requests_test.go @@ -276,3 +276,99 @@ func TestVolumeTypeRemoveAccess(t *testing.T) { th.AssertNoErr(t, err) } + +func TestCreateEncryption(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockEncryptionCreateResponse(t) + + options := &volumetypes.CreateEncryptionOpts{ + KeySize: 256, + Provider: "luks", + ControlLocation: "front-end", + Cipher: "aes-xts-plain64", + } + id := "a5082c24-2a27-43a4-b48e-fcec1240e36b" + n, err := volumetypes.CreateEncryption(client.ServiceClient(), id, options).Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, "a5082c24-2a27-43a4-b48e-fcec1240e36b", n.VolumeTypeID) + th.AssertEquals(t, "front-end", n.ControlLocation) + th.AssertEquals(t, "81e069c6-7394-4856-8df7-3b237ca61f74", n.EncryptionID) + th.AssertEquals(t, 256, n.KeySize) + th.AssertEquals(t, "luks", n.Provider) + th.AssertEquals(t, "aes-xts-plain64", n.Cipher) +} + +func TestDeleteEncryption(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockDeleteEncryptionResponse(t) + + res := volumetypes.DeleteEncryption(client.ServiceClient(), "a5082c24-2a27-43a4-b48e-fcec1240e36b", "81e069c6-7394-4856-8df7-3b237ca61f74" ) + th.AssertNoErr(t, res.Err) +} + +func TestUpdateEncryption(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockEncryptionUpdateResponse(t) + + options := &volumetypes.UpdateEncryptionOpts{ + KeySize: 256, + Provider: "luks", + ControlLocation: "front-end", + Cipher: "aes-xts-plain64", + } + id := "a5082c24-2a27-43a4-b48e-fcec1240e36b" + encryptionID := "81e069c6-7394-4856-8df7-3b237ca61f74" + n, err := volumetypes.UpdateEncryption(client.ServiceClient(), id, encryptionID, options).Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, "front-end", n.ControlLocation) + th.AssertEquals(t, 256, n.KeySize) + th.AssertEquals(t, "luks", n.Provider) + th.AssertEquals(t, "aes-xts-plain64", n.Cipher) +} + +func TestGetEncryption(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockEncryptionGetResponse(t) + id := "a5082c24-2a27-43a4-b48e-fcec1240e36b" + n, err := volumetypes.GetEncryption(client.ServiceClient(), id).Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, "a5082c24-2a27-43a4-b48e-fcec1240e36b", n.VolumeTypeID) + th.AssertEquals(t, "front-end", n.ControlLocation) + th.AssertEquals(t, false, n.Deleted) + th.AssertEquals(t, "2016-12-28T02:32:25.000000", n.CreatedAt) + th.AssertEquals(t, "", n.UpdatedAt) + th.AssertEquals(t, "81e069c6-7394-4856-8df7-3b237ca61f74", n.EncryptionID) + th.AssertEquals(t, 256, n.KeySize) + th.AssertEquals(t, "luks", n.Provider) + th.AssertEquals(t, "", n.DeletedAt) + th.AssertEquals(t, "aes-xts-plain64", n.Cipher) +} + +func TestGetEncryptionSpec(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockEncryptionGetSpecResponse(t) + id := "a5082c24-2a27-43a4-b48e-fcec1240e36b" + n, err := volumetypes.GetEncryptionSpec(client.ServiceClient(), id, "cipher").Extract() + th.AssertNoErr(t, err) + + key := "cipher" + testVar, exists := n[key] + if exists { + th.AssertEquals(t, "aes-xts-plain64", testVar) + } else { + t.Fatalf("Key %s does not exist in map.", key) + } +} diff --git a/openstack/blockstorage/v3/volumetypes/urls.go b/openstack/blockstorage/v3/volumetypes/urls.go index c63ee47e62..9ba503d314 100644 --- a/openstack/blockstorage/v3/volumetypes/urls.go +++ b/openstack/blockstorage/v3/volumetypes/urls.go @@ -49,3 +49,23 @@ func accessURL(client *gophercloud.ServiceClient, id string) string { func accessActionURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("types", id, "action") } + +func createEncryptionURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL("types", id, "encryption") +} + +func deleteEncryptionURL(client *gophercloud.ServiceClient, id, encryptionID string) string { + return client.ServiceURL("types", id, "encryption", encryptionID) +} + +func getEncryptionURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL("types", id, "encryption") +} + +func getEncryptionSpecURL(client *gophercloud.ServiceClient, id, key string) string { + return client.ServiceURL("types", id, "encryption", key) +} + +func updateEncryptionURL(client *gophercloud.ServiceClient, id, encryptionID string) string { + return client.ServiceURL("types", id, "encryption", encryptionID) +} From 8353428d01a350ac677cc12d20d25937d0ea754f Mon Sep 17 00:00:00 2001 From: georgeb <george.bampilis@surf.nl> Date: Mon, 12 Jun 2023 11:32:41 +0200 Subject: [PATCH 273/360] fixup: Correct some acceptance test errors --- .../openstack/blockstorage/v3/volumetypes_test.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/acceptance/openstack/blockstorage/v3/volumetypes_test.go b/acceptance/openstack/blockstorage/v3/volumetypes_test.go index 73fa32722a..c01218d51f 100644 --- a/acceptance/openstack/blockstorage/v3/volumetypes_test.go +++ b/acceptance/openstack/blockstorage/v3/volumetypes_test.go @@ -174,16 +174,16 @@ func TestEncryptionVolumeTypes(t *testing.T) { th.AssertNoErr(t, err) defer DeleteVolumeType(t, client, vt) - createEncryptionOpts := volumetypes.EncryptionOptions{ + createEncryptionOpts := volumetypes.CreateEncryptionOpts{ KeySize: 256, Provider: "luks", ControlLocation: "front-end", Cipher: "aes-xts-plain64", } - eVT, err := volumetypes.CreateEncryption(client, vt.ID, createEncryptionOpts) + eVT, err := volumetypes.CreateEncryption(client, vt.ID, createEncryptionOpts).Extract() th.AssertNoErr(t, err) - defer DeleteEncryption(t, client, evt) + defer volumetypes.DeleteEncryption(client, eVT.VolumeTypeID, eVT.EncryptionID) geVT, err := volumetypes.GetEncryption(client, vt.ID).Extract() th.AssertNoErr(t, err) @@ -195,13 +195,13 @@ func TestEncryptionVolumeTypes(t *testing.T) { tools.PrintResource(t, gesVT) - updateEncryptionOpts := volumetypes.EncryptionUpdateOpts{ + updateEncryptionOpts := volumetypes.UpdateEncryptionOpts{ ControlLocation: "back-end", } - newEVT, err := volumetypes.Update(client, vt.ID, updateEncryptionOpts).Extract() + newEVT, err := volumetypes.UpdateEncryption(client, vt.ID, eVT.EncryptionID, updateEncryptionOpts).Extract() + tools.PrintResource(t, newEVT) th.AssertNoErr(t, err) - tools.PrintResource(t, newEVT) - th.AssertEquals(t, "back-end", newEVT.ControlLocation) + th.AssertEquals(t, "back-end", newEVT.ControlLocation) } From 71924994cbe408a2600b7a9f045fcd5f521ed5eb Mon Sep 17 00:00:00 2001 From: georgeb <george.bampilis@surf.nl> Date: Tue, 20 Jun 2023 17:29:13 +0200 Subject: [PATCH 274/360] fixup: Correct some unit test errors --- .../blockstorage/v3/volumetypes_test.go | 31 +++++----- openstack/blockstorage/v3/volumetypes/doc.go | 52 ++++++++-------- .../blockstorage/v3/volumetypes/requests.go | 36 +++++------ .../blockstorage/v3/volumetypes/results.go | 42 ++++++------- .../v3/volumetypes/testing/fixtures.go | 4 +- .../v3/volumetypes/testing/requests_test.go | 62 +++++++++---------- openstack/blockstorage/v3/volumetypes/urls.go | 8 +-- 7 files changed, 115 insertions(+), 120 deletions(-) diff --git a/acceptance/openstack/blockstorage/v3/volumetypes_test.go b/acceptance/openstack/blockstorage/v3/volumetypes_test.go index c01218d51f..6ac7086c3a 100644 --- a/acceptance/openstack/blockstorage/v3/volumetypes_test.go +++ b/acceptance/openstack/blockstorage/v3/volumetypes_test.go @@ -174,34 +174,33 @@ func TestEncryptionVolumeTypes(t *testing.T) { th.AssertNoErr(t, err) defer DeleteVolumeType(t, client, vt) - createEncryptionOpts := volumetypes.CreateEncryptionOpts{ - KeySize: 256, - Provider: "luks", - ControlLocation: "front-end", - Cipher: "aes-xts-plain64", - } + createEncryptionOpts := volumetypes.CreateEncryptionOpts{ + KeySize: 256, + Provider: "luks", + ControlLocation: "front-end", + Cipher: "aes-xts-plain64", + } - eVT, err := volumetypes.CreateEncryption(client, vt.ID, createEncryptionOpts).Extract() - th.AssertNoErr(t, err) - defer volumetypes.DeleteEncryption(client, eVT.VolumeTypeID, eVT.EncryptionID) + eVT, err := volumetypes.CreateEncryption(client, vt.ID, createEncryptionOpts).Extract() + th.AssertNoErr(t, err) + defer volumetypes.DeleteEncryption(client, eVT.VolumeTypeID, eVT.EncryptionID) geVT, err := volumetypes.GetEncryption(client, vt.ID).Extract() th.AssertNoErr(t, err) - tools.PrintResource(t, geVT) + tools.PrintResource(t, geVT) - key := "cipher" + key := "cipher" gesVT, err := volumetypes.GetEncryptionSpec(client, vt.ID, key).Extract() th.AssertNoErr(t, err) - tools.PrintResource(t, gesVT) - + tools.PrintResource(t, gesVT) updateEncryptionOpts := volumetypes.UpdateEncryptionOpts{ - ControlLocation: "back-end", + ControlLocation: "back-end", } newEVT, err := volumetypes.UpdateEncryption(client, vt.ID, eVT.EncryptionID, updateEncryptionOpts).Extract() - tools.PrintResource(t, newEVT) + tools.PrintResource(t, newEVT) th.AssertNoErr(t, err) - th.AssertEquals(t, "back-end", newEVT.ControlLocation) + th.AssertEquals(t, "back-end", newEVT.ControlLocation) } diff --git a/openstack/blockstorage/v3/volumetypes/doc.go b/openstack/blockstorage/v3/volumetypes/doc.go index d532e7262a..03cad7ecbd 100644 --- a/openstack/blockstorage/v3/volumetypes/doc.go +++ b/openstack/blockstorage/v3/volumetypes/doc.go @@ -162,26 +162,27 @@ Example to Remove/Revoke Access to a Volume Type } Example to Create the Encryption of a Volume Type - typeID := "7ffaca22-f646-41d4-b79d-d7e4452ef8cc" - volumeType, err := volumetypes.CreateEncryption(client, typeID, .CreateEncryptionOpts{ - KeySize: 256, - Provider: "luks", - ControlLocation: "front-end", - Cipher: "aes-xts-plain64", - }).Extract() - if err != nil{ - panic(err) - } - fmt.Println(volumeType) + + typeID := "7ffaca22-f646-41d4-b79d-d7e4452ef8cc" + volumeType, err := volumetypes.CreateEncryption(client, typeID, .CreateEncryptionOpts{ + KeySize: 256, + Provider: "luks", + ControlLocation: "front-end", + Cipher: "aes-xts-plain64", + }).Extract() + if err != nil{ + panic(err) + } + fmt.Println(volumeType) Example to Delete the Encryption of a Volume Type - typeID := "7ffaca22-f646-41d4-b79d-d7e4452ef8cc" - encryptionID := ""81e069c6-7394-4856-8df7-3b237ca61f74 - err := volumetypes.DeleteEncryption(client, typeID, encryptionID).ExtractErr() - if err != nil{ - panic(err) - } + typeID := "7ffaca22-f646-41d4-b79d-d7e4452ef8cc" + encryptionID := ""81e069c6-7394-4856-8df7-3b237ca61f74 + err := volumetypes.DeleteEncryption(client, typeID, encryptionID).ExtractErr() + if err != nil{ + panic(err) + } Example to Update the Encryption of a Volume Type @@ -208,15 +209,12 @@ Example to Show an Encryption of a Volume Type Example to Show an Encryption Spec of a Volume Type - typeID := "7ffaca22-f646-41d4-b79d-d7e4452ef8cc" - key := "cipher" - volumeType, err := volumetypes.GetEncrytpionSpec(client, typeID).Extract() - if err != nil{ - panic(err) - } - fmt.Println(volumeType) - - - + typeID := "7ffaca22-f646-41d4-b79d-d7e4452ef8cc" + key := "cipher" + volumeType, err := volumetypes.GetEncrytpionSpec(client, typeID).Extract() + if err != nil{ + panic(err) + } + fmt.Println(volumeType) */ package volumetypes diff --git a/openstack/blockstorage/v3/volumetypes/requests.go b/openstack/blockstorage/v3/volumetypes/requests.go index 4b7e561bc2..e06f7a4638 100644 --- a/openstack/blockstorage/v3/volumetypes/requests.go +++ b/openstack/blockstorage/v3/volumetypes/requests.go @@ -315,14 +315,14 @@ type CreateEncryptionOptsBuilder interface { // This object is passed to the volumetypes.CreateEncryption function. // For more information about these parameters,see the Encryption Type object. type CreateEncryptionOpts struct { - // The size of the encryption key. - KeySize int `json:"key_size"` - // The class of that provides the encryption support. - Provider string `json:"provider" required:"true"` - // Notional service where encryption is performed. - ControlLocation string `json:"control_location"` - // The encryption algorithm or mode. - Cipher string `json:"cipher"` + // The size of the encryption key. + KeySize int `json:"key_size"` + // The class of that provides the encryption support. + Provider string `json:"provider" required:"true"` + // Notional service where encryption is performed. + ControlLocation string `json:"control_location"` + // The encryption algorithm or mode. + Cipher string `json:"cipher"` } // ToEncryptionCreateMap assembles a request body based on the contents of a @@ -355,7 +355,7 @@ func DeleteEncryption(client *gophercloud.ServiceClient, id, encryptionID string } // GetEncryption retrieves the encryption type for an existing VolumeType with the provided ID. -func GetEncryption(client *gophercloud.ServiceClient, id string) (r GetEncryptionResult){ +func GetEncryption(client *gophercloud.ServiceClient, id string) (r GetEncryptionResult) { resp, err := client.Get(getEncryptionURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return @@ -378,14 +378,14 @@ type UpdateEncryptionOptsBuilder interface { // the volumetypes.UpdateEncryption function. For more information about these parameters, // see the Update Encryption Type object. type UpdateEncryptionOpts struct { - // The size of the encryption key. - KeySize int `json:"key_size"` - // The class of that provides the encryption support. - Provider string `json:"provider"` - // Notional service where encryption is performed. - ControlLocation string `json:"control_location"` - // The encryption algorithm or mode. - Cipher string `json:"cipher"` + // The size of the encryption key. + KeySize int `json:"key_size"` + // The class of that provides the encryption support. + Provider string `json:"provider"` + // Notional service where encryption is performed. + ControlLocation string `json:"control_location"` + // The encryption algorithm or mode. + Cipher string `json:"cipher"` } // ToEncryptionCreateMap assembles a request body based on the contents of a @@ -397,7 +397,7 @@ func (opts UpdateEncryptionOpts) ToUpdateEncryptionMap() (map[string]interface{} // Update will update an existing encryption for a Volume Type based on the values in UpdateEncryptionOpts. // To extract the UpdateEncryption Type object from the response, call the Extract method on the // UpdateEncryptionResult. -func UpdateEncryption(client *gophercloud.ServiceClient, id,encryptionID string, opts UpdateEncryptionOptsBuilder) (r UpdateEncryptionResult) { +func UpdateEncryption(client *gophercloud.ServiceClient, id, encryptionID string, opts UpdateEncryptionOptsBuilder) (r UpdateEncryptionResult) { b, err := opts.ToUpdateEncryptionMap() if err != nil { r.Err = err diff --git a/openstack/blockstorage/v3/volumetypes/results.go b/openstack/blockstorage/v3/volumetypes/results.go index e4ff961ace..4d1d1cf2df 100644 --- a/openstack/blockstorage/v3/volumetypes/results.go +++ b/openstack/blockstorage/v3/volumetypes/results.go @@ -201,14 +201,13 @@ type RemoveAccessResult struct { gophercloud.ErrResult } - type EncryptionType struct { // Unique identifier for the volume type. - VolumeTypeID string `json:"volume_type_id"` + VolumeTypeID string `json:"volume_type_id"` // Notional service where encryption is performed. ControlLocation string `json:"control_location"` // Unique identifier for encryption type. - EncryptionID string `json:"encryption_id"` + EncryptionID string `json:"encryption_id"` // Size of encryption key. KeySize int `json:"key_size"` // Class that provides encryption support. @@ -247,25 +246,25 @@ type DeleteEncryptionResult struct { } type GetEncryptionType struct { - // Unique identifier for the volume type. - VolumeTypeID string `json:"volume_type_id"` + // Unique identifier for the volume type. + VolumeTypeID string `json:"volume_type_id"` // Notional service where encryption is performed. ControlLocation string `json:"control_location"` - // Shows if the resource is deleted or Notional - Deleted bool `json:"deleted"` - // Shows the date and time the resource was created. - CreatedAt string `json:"created_at"` - // Shows the date and time when resource was updated. - UpdatedAt string `json:"updated_at"` - // Unique identifier for encryption type. - EncryptionID string `json:"encryption_id"` + // Shows if the resource is deleted or Notional + Deleted bool `json:"deleted"` + // Shows the date and time the resource was created. + CreatedAt string `json:"created_at"` + // Shows the date and time when resource was updated. + UpdatedAt string `json:"updated_at"` + // Unique identifier for encryption type. + EncryptionID string `json:"encryption_id"` // Size of encryption key. KeySize int `json:"key_size"` // Class that provides encryption support. Provider string `json:"provider"` - // Shows the date and time the reousrce was deleted. - DeletedAt string `json:"deleted_at"` - // The encryption algorithm or mode. + // Shows the date and time the reousrce was deleted. + DeletedAt string `json:"deleted_at"` + // The encryption algorithm or mode. Cipher string `json:"cipher"` } @@ -274,7 +273,7 @@ type encryptionShowResult struct { } // Extract interprets any extraSpecResult as an ExtraSpec, if possible. -func (r encryptionShowResult) Extract() (* GetEncryptionType, error) { +func (r encryptionShowResult) Extract() (*GetEncryptionType, error) { var s GetEncryptionType err := r.ExtractInto(&s) return &s, err @@ -284,8 +283,8 @@ type GetEncryptionResult struct { encryptionShowResult } -type encryptionShowSpecResult struct{ - gophercloud.Result +type encryptionShowSpecResult struct { + gophercloud.Result } // Extract interprets any empty interface Result as an empty interface. @@ -295,7 +294,6 @@ func (r encryptionShowSpecResult) Extract() (map[string]interface{}, error) { return s, err } -type GetEncryptionSpecResult struct{ - encryptionShowSpecResult +type GetEncryptionSpecResult struct { + encryptionShowSpecResult } - diff --git a/openstack/blockstorage/v3/volumetypes/testing/fixtures.go b/openstack/blockstorage/v3/volumetypes/testing/fixtures.go index bee9cee1e0..a8cb0345cc 100644 --- a/openstack/blockstorage/v3/volumetypes/testing/fixtures.go +++ b/openstack/blockstorage/v3/volumetypes/testing/fixtures.go @@ -340,7 +340,7 @@ func MockEncryptionGetResponse(t *testing.T) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - w.Header().Add("Content-Type", "application/json") + w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` @@ -365,7 +365,7 @@ func MockEncryptionGetSpecResponse(t *testing.T) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - w.Header().Add("Content-Type", "application/json") + w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` diff --git a/openstack/blockstorage/v3/volumetypes/testing/requests_test.go b/openstack/blockstorage/v3/volumetypes/testing/requests_test.go index f3d6098490..033158df55 100644 --- a/openstack/blockstorage/v3/volumetypes/testing/requests_test.go +++ b/openstack/blockstorage/v3/volumetypes/testing/requests_test.go @@ -284,13 +284,13 @@ func TestCreateEncryption(t *testing.T) { MockEncryptionCreateResponse(t) options := &volumetypes.CreateEncryptionOpts{ - KeySize: 256, - Provider: "luks", + KeySize: 256, + Provider: "luks", ControlLocation: "front-end", - Cipher: "aes-xts-plain64", + Cipher: "aes-xts-plain64", } - id := "a5082c24-2a27-43a4-b48e-fcec1240e36b" - n, err := volumetypes.CreateEncryption(client.ServiceClient(), id, options).Extract() + id := "a5082c24-2a27-43a4-b48e-fcec1240e36b" + n, err := volumetypes.CreateEncryption(client.ServiceClient(), id, options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, "a5082c24-2a27-43a4-b48e-fcec1240e36b", n.VolumeTypeID) @@ -298,7 +298,7 @@ func TestCreateEncryption(t *testing.T) { th.AssertEquals(t, "81e069c6-7394-4856-8df7-3b237ca61f74", n.EncryptionID) th.AssertEquals(t, 256, n.KeySize) th.AssertEquals(t, "luks", n.Provider) - th.AssertEquals(t, "aes-xts-plain64", n.Cipher) + th.AssertEquals(t, "aes-xts-plain64", n.Cipher) } func TestDeleteEncryption(t *testing.T) { @@ -307,7 +307,7 @@ func TestDeleteEncryption(t *testing.T) { MockDeleteEncryptionResponse(t) - res := volumetypes.DeleteEncryption(client.ServiceClient(), "a5082c24-2a27-43a4-b48e-fcec1240e36b", "81e069c6-7394-4856-8df7-3b237ca61f74" ) + res := volumetypes.DeleteEncryption(client.ServiceClient(), "a5082c24-2a27-43a4-b48e-fcec1240e36b", "81e069c6-7394-4856-8df7-3b237ca61f74") th.AssertNoErr(t, res.Err) } @@ -318,20 +318,20 @@ func TestUpdateEncryption(t *testing.T) { MockEncryptionUpdateResponse(t) options := &volumetypes.UpdateEncryptionOpts{ - KeySize: 256, - Provider: "luks", + KeySize: 256, + Provider: "luks", ControlLocation: "front-end", - Cipher: "aes-xts-plain64", + Cipher: "aes-xts-plain64", } - id := "a5082c24-2a27-43a4-b48e-fcec1240e36b" - encryptionID := "81e069c6-7394-4856-8df7-3b237ca61f74" - n, err := volumetypes.UpdateEncryption(client.ServiceClient(), id, encryptionID, options).Extract() + id := "a5082c24-2a27-43a4-b48e-fcec1240e36b" + encryptionID := "81e069c6-7394-4856-8df7-3b237ca61f74" + n, err := volumetypes.UpdateEncryption(client.ServiceClient(), id, encryptionID, options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, "front-end", n.ControlLocation) th.AssertEquals(t, 256, n.KeySize) th.AssertEquals(t, "luks", n.Provider) - th.AssertEquals(t, "aes-xts-plain64", n.Cipher) + th.AssertEquals(t, "aes-xts-plain64", n.Cipher) } func TestGetEncryption(t *testing.T) { @@ -339,20 +339,20 @@ func TestGetEncryption(t *testing.T) { defer th.TeardownHTTP() MockEncryptionGetResponse(t) - id := "a5082c24-2a27-43a4-b48e-fcec1240e36b" - n, err := volumetypes.GetEncryption(client.ServiceClient(), id).Extract() + id := "a5082c24-2a27-43a4-b48e-fcec1240e36b" + n, err := volumetypes.GetEncryption(client.ServiceClient(), id).Extract() th.AssertNoErr(t, err) - th.AssertEquals(t, "a5082c24-2a27-43a4-b48e-fcec1240e36b", n.VolumeTypeID) + th.AssertEquals(t, "a5082c24-2a27-43a4-b48e-fcec1240e36b", n.VolumeTypeID) th.AssertEquals(t, "front-end", n.ControlLocation) th.AssertEquals(t, false, n.Deleted) - th.AssertEquals(t, "2016-12-28T02:32:25.000000", n.CreatedAt) - th.AssertEquals(t, "", n.UpdatedAt) - th.AssertEquals(t, "81e069c6-7394-4856-8df7-3b237ca61f74", n.EncryptionID) + th.AssertEquals(t, "2016-12-28T02:32:25.000000", n.CreatedAt) + th.AssertEquals(t, "", n.UpdatedAt) + th.AssertEquals(t, "81e069c6-7394-4856-8df7-3b237ca61f74", n.EncryptionID) th.AssertEquals(t, 256, n.KeySize) th.AssertEquals(t, "luks", n.Provider) - th.AssertEquals(t, "", n.DeletedAt) - th.AssertEquals(t, "aes-xts-plain64", n.Cipher) + th.AssertEquals(t, "", n.DeletedAt) + th.AssertEquals(t, "aes-xts-plain64", n.Cipher) } func TestGetEncryptionSpec(t *testing.T) { @@ -360,15 +360,15 @@ func TestGetEncryptionSpec(t *testing.T) { defer th.TeardownHTTP() MockEncryptionGetSpecResponse(t) - id := "a5082c24-2a27-43a4-b48e-fcec1240e36b" - n, err := volumetypes.GetEncryptionSpec(client.ServiceClient(), id, "cipher").Extract() + id := "a5082c24-2a27-43a4-b48e-fcec1240e36b" + n, err := volumetypes.GetEncryptionSpec(client.ServiceClient(), id, "cipher").Extract() th.AssertNoErr(t, err) - key := "cipher" - testVar, exists := n[key] - if exists { - th.AssertEquals(t, "aes-xts-plain64", testVar) - } else { - t.Fatalf("Key %s does not exist in map.", key) - } + key := "cipher" + testVar, exists := n[key] + if exists { + th.AssertEquals(t, "aes-xts-plain64", testVar) + } else { + t.Fatalf("Key %s does not exist in map.", key) + } } diff --git a/openstack/blockstorage/v3/volumetypes/urls.go b/openstack/blockstorage/v3/volumetypes/urls.go index 9ba503d314..c65478e684 100644 --- a/openstack/blockstorage/v3/volumetypes/urls.go +++ b/openstack/blockstorage/v3/volumetypes/urls.go @@ -55,17 +55,17 @@ func createEncryptionURL(client *gophercloud.ServiceClient, id string) string { } func deleteEncryptionURL(client *gophercloud.ServiceClient, id, encryptionID string) string { - return client.ServiceURL("types", id, "encryption", encryptionID) + return client.ServiceURL("types", id, "encryption", encryptionID) } func getEncryptionURL(client *gophercloud.ServiceClient, id string) string { - return client.ServiceURL("types", id, "encryption") + return client.ServiceURL("types", id, "encryption") } func getEncryptionSpecURL(client *gophercloud.ServiceClient, id, key string) string { - return client.ServiceURL("types", id, "encryption", key) + return client.ServiceURL("types", id, "encryption", key) } func updateEncryptionURL(client *gophercloud.ServiceClient, id, encryptionID string) string { - return client.ServiceURL("types", id, "encryption", encryptionID) + return client.ServiceURL("types", id, "encryption", encryptionID) } From 90064d7c1679a896482d86f5dead95149a97e55b Mon Sep 17 00:00:00 2001 From: nikParasyr <nik.parasyr@protonmail.com> Date: Thu, 29 Jun 2023 23:12:45 +0200 Subject: [PATCH 275/360] Add projectID to fwaas_v2 policy CreateOpts and ListOpts --- .../v2/extensions/fwaas_v2/policies/requests.go | 2 ++ .../v2/extensions/fwaas_v2/policies/results.go | 1 + .../fwaas_v2/policies/testing/requests_test.go | 12 +++++++++++- 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/openstack/networking/v2/extensions/fwaas_v2/policies/requests.go b/openstack/networking/v2/extensions/fwaas_v2/policies/requests.go index d5a701aa13..8f036ca93c 100644 --- a/openstack/networking/v2/extensions/fwaas_v2/policies/requests.go +++ b/openstack/networking/v2/extensions/fwaas_v2/policies/requests.go @@ -18,6 +18,7 @@ type ListOptsBuilder interface { // and is either `asc' or `desc'. Marker and Limit are used for pagination. type ListOpts struct { TenantID string `q:"tenant_id"` + ProjectID string `q:"project_id"` Name string `q:"name"` Description string `q:"description"` Shared *bool `q:"shared"` @@ -68,6 +69,7 @@ type CreateOpts struct { // Only required if the caller has an admin role and wants to create a firewall policy // for another tenant. TenantID string `json:"tenant_id,omitempty"` + ProjectID string `json:"project_id,omitempty"` Name string `json:"name,omitempty"` Description string `json:"description,omitempty"` Shared *bool `json:"shared,omitempty"` diff --git a/openstack/networking/v2/extensions/fwaas_v2/policies/results.go b/openstack/networking/v2/extensions/fwaas_v2/policies/results.go index c0139aea3f..8d7411c3de 100644 --- a/openstack/networking/v2/extensions/fwaas_v2/policies/results.go +++ b/openstack/networking/v2/extensions/fwaas_v2/policies/results.go @@ -11,6 +11,7 @@ type Policy struct { Name string `json:"name"` Description string `json:"description"` TenantID string `json:"tenant_id"` + ProjectID string `json:"project_id"` Audited bool `json:"audited"` Shared bool `json:"shared"` Rules []string `json:"firewall_rules,omitempty"` diff --git a/openstack/networking/v2/extensions/fwaas_v2/policies/testing/requests_test.go b/openstack/networking/v2/extensions/fwaas_v2/policies/testing/requests_test.go index ca0e09263c..e0251c770a 100644 --- a/openstack/networking/v2/extensions/fwaas_v2/policies/testing/requests_test.go +++ b/openstack/networking/v2/extensions/fwaas_v2/policies/testing/requests_test.go @@ -33,6 +33,7 @@ func TestList(t *testing.T) { "c9e77ca0-1bc8-497d-904d-948107873dc6" ], "tenant_id": "9145d91459d248b1b02fdaca97c6a75d", + "project_id": "9145d91459d248b1b02fdaca97c6a75d", "audited": true, "shared": false, "id": "f2b08c1e-aa81-4668-8ae1-1401bcb0576c", @@ -44,6 +45,7 @@ func TestList(t *testing.T) { "03d2a6ad-633f-431a-8463-4370d06a22c8" ], "tenant_id": "9145d91459d248b1b02fdaca97c6a75d", + "project_id": "9145d91459d248b1b02fdaca97c6a75d", "audited": false, "shared": true, "id": "c854fab5-bdaf-4a86-9359-78de93e5df01", @@ -72,6 +74,7 @@ func TestList(t *testing.T) { "c9e77ca0-1bc8-497d-904d-948107873dc6", }, TenantID: "9145d91459d248b1b02fdaca97c6a75d", + ProjectID: "9145d91459d248b1b02fdaca97c6a75d", Audited: true, Shared: false, ID: "f2b08c1e-aa81-4668-8ae1-1401bcb0576c", @@ -83,6 +86,7 @@ func TestList(t *testing.T) { "03d2a6ad-633f-431a-8463-4370d06a22c8", }, TenantID: "9145d91459d248b1b02fdaca97c6a75d", + ProjectID: "9145d91459d248b1b02fdaca97c6a75d", Audited: false, Shared: true, ID: "c854fab5-bdaf-4a86-9359-78de93e5df01", @@ -119,6 +123,7 @@ func TestCreate(t *testing.T) { ], "description": "Firewall policy", "tenant_id": "9145d91459d248b1b02fdaca97c6a75d", + "project_id": "9145d91459d248b1b02fdaca97c6a75d", "audited": true, "shared": false } @@ -137,6 +142,7 @@ func TestCreate(t *testing.T) { "11a58c87-76be-ae7c-a74e-b77fffb88a32" ], "tenant_id": "9145d91459d248b1b02fdaca97c6a75d", + "project_id": "9145d91459d248b1b02fdaca97c6a75d", "audited": false, "id": "f2b08c1e-aa81-4668-8ae1-1401bcb0576c", "description": "Firewall policy" @@ -147,6 +153,7 @@ func TestCreate(t *testing.T) { options := policies.CreateOpts{ TenantID: "9145d91459d248b1b02fdaca97c6a75d", + ProjectID: "9145d91459d248b1b02fdaca97c6a75d", Name: "policy", Description: "Firewall policy", Shared: gophercloud.Disabled, @@ -211,7 +218,7 @@ func TestInsertRule(t *testing.T) { th.AssertEquals(t, "e3c78ab6-e827-4297-8d68-739063865a8b", policy.ID) th.AssertEquals(t, "TESTACC-DESC-8P12aLfW", policy.Description) th.AssertEquals(t, "9f98fc0e5f944cd1b51798b668dc8778", policy.TenantID) - + th.AssertEquals(t, "9f98fc0e5f944cd1b51798b668dc8778", policy.ProjectID) } func TestInsertRuleWithInvalidParameters(t *testing.T) { @@ -253,6 +260,7 @@ func TestGet(t *testing.T) { "03d2a6ad-633f-431a-8463-4370d06a22c8" ], "tenant_id": "9145d91459d248b1b02fdaca97c6a75d", + "project_id": "9145d91459d248b1b02fdaca97c6a75d", "audited": false, "id": "f2b08c1e-aa81-4668-8ae1-1401bcb0576c", "description": "Firewall policy web" @@ -272,6 +280,7 @@ func TestGet(t *testing.T) { th.AssertEquals(t, "c9e77ca0-1bc8-497d-904d-948107873dc6", policy.Rules[1]) th.AssertEquals(t, "03d2a6ad-633f-431a-8463-4370d06a22c8", policy.Rules[2]) th.AssertEquals(t, "9145d91459d248b1b02fdaca97c6a75d", policy.TenantID) + th.AssertEquals(t, "9145d91459d248b1b02fdaca97c6a75d", policy.ProjectID) } func TestUpdate(t *testing.T) { @@ -309,6 +318,7 @@ func TestUpdate(t *testing.T) { "03d2a6ad-633f-431a-8463-4370d06a22c8" ], "tenant_id": "9145d91459d248b1b02fdaca97c6a75d", + "project_id": "9145d91459d248b1b02fdaca97c6a75d", "audited": false, "id": "f2b08c1e-aa81-4668-8ae1-1401bcb0576c", "description": "Firewall policy" From f81b74488e8a3bec3371934392b19abe0670cb74 Mon Sep 17 00:00:00 2001 From: nikParasyr <nik.parasyr@protonmail.com> Date: Thu, 29 Jun 2023 22:42:30 +0200 Subject: [PATCH 276/360] Add projectID to fwaas_v2 CreateOpts --- .../v2/extensions/fwaas_v2/rules/requests.go | 1 + .../fwaas_v2/rules/testing/requests_test.go | 17 +++++++++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/openstack/networking/v2/extensions/fwaas_v2/rules/requests.go b/openstack/networking/v2/extensions/fwaas_v2/rules/requests.go index 7ffd681d96..d80e682796 100644 --- a/openstack/networking/v2/extensions/fwaas_v2/rules/requests.go +++ b/openstack/networking/v2/extensions/fwaas_v2/rules/requests.go @@ -117,6 +117,7 @@ type CreateOpts struct { Protocol Protocol `json:"protocol" required:"true"` Action Action `json:"action" required:"true"` TenantID string `json:"tenant_id,omitempty"` + ProjectID string `json:"project_id,omitempty"` Name string `json:"name,omitempty"` Description string `json:"description,omitempty"` IPVersion gophercloud.IPVersion `json:"ip_version,omitempty"` diff --git a/openstack/networking/v2/extensions/fwaas_v2/rules/testing/requests_test.go b/openstack/networking/v2/extensions/fwaas_v2/rules/testing/requests_test.go index 7330833654..8ca0d70e28 100644 --- a/openstack/networking/v2/extensions/fwaas_v2/rules/testing/requests_test.go +++ b/openstack/networking/v2/extensions/fwaas_v2/rules/testing/requests_test.go @@ -37,6 +37,7 @@ func TestList(t *testing.T) { "id": "f03bd950-6c56-4f5e-a307-45967078f507", "name": "ssh_form_any", "tenant_id": "80cf934d6ffb4ef5b244f1c512ad1e61", + "project_id": "80cf934d6ffb4ef5b244f1c512ad1e61", "enabled": true, "action": "allow", "ip_version": 4, @@ -53,6 +54,7 @@ func TestList(t *testing.T) { "id": "ab7bd950-6c56-4f5e-a307-45967078f890", "name": "deny_all_udp", "tenant_id": "80cf934d6ffb4ef5b244f1c512ad1e61", + "project_id": "80cf934d6ffb4ef5b244f1c512ad1e61", "enabled": true, "action": "deny", "ip_version": 4, @@ -85,6 +87,7 @@ func TestList(t *testing.T) { ID: "f03bd950-6c56-4f5e-a307-45967078f507", Name: "ssh_form_any", TenantID: "80cf934d6ffb4ef5b244f1c512ad1e61", + ProjectID: "80cf934d6ffb4ef5b244f1c512ad1e61", Enabled: true, Action: string(rules.ActionAllow), IPVersion: 4, @@ -101,6 +104,7 @@ func TestList(t *testing.T) { ID: "ab7bd950-6c56-4f5e-a307-45967078f890", Name: "deny_all_udp", TenantID: "80cf934d6ffb4ef5b244f1c512ad1e61", + ProjectID: "80cf934d6ffb4ef5b244f1c512ad1e61", Enabled: true, Action: "deny", IPVersion: 4, @@ -135,7 +139,8 @@ func TestCreate(t *testing.T) { "destination_port": "22", "name": "ssh_form_any", "action": "allow", - "tenant_id": "80cf934d6ffb4ef5b244f1c512ad1e61" + "tenant_id": "80cf934d6ffb4ef5b244f1c512ad1e61", + "project_id": "80cf934d6ffb4ef5b244f1c512ad1e61" } } `) @@ -157,6 +162,7 @@ func TestCreate(t *testing.T) { "id": "f03bd950-6c56-4f5e-a307-45967078f507", "name": "ssh_form_any", "tenant_id": "80cf934d6ffb4ef5b244f1c512ad1e61", + "project_id": "80cf934d6ffb4ef5b244f1c512ad1e61", "enabled": true, "action": "allow", "ip_version": 4, @@ -168,6 +174,7 @@ func TestCreate(t *testing.T) { options := rules.CreateOpts{ TenantID: "80cf934d6ffb4ef5b244f1c512ad1e61", + ProjectID: "80cf934d6ffb4ef5b244f1c512ad1e61", Protocol: rules.ProtocolTCP, Description: "ssh rule", DestinationIPAddress: "192.168.1.0/24", @@ -197,7 +204,8 @@ func TestCreateAnyProtocol(t *testing.T) { "destination_ip_address": "192.168.1.0/24", "name": "any_to_192.168.1.0/24", "action": "allow", - "tenant_id": "80cf934d6ffb4ef5b244f1c512ad1e61" + "tenant_id": "80cf934d6ffb4ef5b244f1c512ad1e61", + "project_id": "80cf934d6ffb4ef5b244f1c512ad1e61" } } `) @@ -219,6 +227,7 @@ func TestCreateAnyProtocol(t *testing.T) { "id": "f03bd950-6c56-4f5e-a307-45967078f507", "name": "any_to_192.168.1.0/24", "tenant_id": "80cf934d6ffb4ef5b244f1c512ad1e61", + "project_id": "80cf934d6ffb4ef5b244f1c512ad1e61", "enabled": true, "action": "allow", "ip_version": 4, @@ -230,6 +239,7 @@ func TestCreateAnyProtocol(t *testing.T) { options := rules.CreateOpts{ TenantID: "80cf934d6ffb4ef5b244f1c512ad1e61", + ProjectID: "80cf934d6ffb4ef5b244f1c512ad1e61", Protocol: rules.ProtocolAny, Description: "any to 192.168.1.0/24", DestinationIPAddress: "192.168.1.0/24", @@ -266,6 +276,7 @@ func TestGet(t *testing.T) { "id": "f03bd950-6c56-4f5e-a307-45967078f507", "name": "ssh_form_any", "tenant_id": "80cf934d6ffb4ef5b244f1c512ad1e61", + "project_id": "80cf934d6ffb4ef5b244f1c512ad1e61", "enabled": true, "action": "allow", "ip_version": 4, @@ -287,6 +298,7 @@ func TestGet(t *testing.T) { th.AssertEquals(t, "f03bd950-6c56-4f5e-a307-45967078f507", rule.ID) th.AssertEquals(t, "ssh_form_any", rule.Name) th.AssertEquals(t, "80cf934d6ffb4ef5b244f1c512ad1e61", rule.TenantID) + th.AssertEquals(t, "80cf934d6ffb4ef5b244f1c512ad1e61", rule.ProjectID) th.AssertEquals(t, true, rule.Enabled) th.AssertEquals(t, "allow", rule.Action) th.AssertEquals(t, 4, rule.IPVersion) @@ -331,6 +343,7 @@ func TestUpdate(t *testing.T) { "id": "f03bd950-6c56-4f5e-a307-45967078f507", "name": "ssh_form_any", "tenant_id": "80cf934d6ffb4ef5b244f1c512ad1e61", + "project_id": "80cf934d6ffb4ef5b244f1c512ad1e61", "enabled": false, "action": "allow", "ip_version": 4, From bf9a309975f7fe2846f12d29fd844a7c20d37fff Mon Sep 17 00:00:00 2001 From: kayrus <kay.diam@gmail.com> Date: Wed, 12 Jul 2023 20:40:46 +0200 Subject: [PATCH 277/360] [manila]: add reset and force delete actions to a snapshot --- .../sharedfilesystems/v2/replicas_test.go | 15 ++-- .../sharedfilesystems/v2/snapshots.go | 3 + .../sharedfilesystems/v2/snapshots_test.go | 75 +++++++++++++++++++ .../v2/snapshots/requests.go | 51 +++++++++++++ .../sharedfilesystems/v2/snapshots/results.go | 10 +++ .../v2/snapshots/testing/fixtures.go | 36 +++++++++ .../v2/snapshots/testing/request_test.go | 24 ++++++ .../sharedfilesystems/v2/snapshots/urls.go | 8 ++ 8 files changed, 215 insertions(+), 7 deletions(-) diff --git a/acceptance/openstack/sharedfilesystems/v2/replicas_test.go b/acceptance/openstack/sharedfilesystems/v2/replicas_test.go index d3179b199f..6fa28cada6 100644 --- a/acceptance/openstack/sharedfilesystems/v2/replicas_test.go +++ b/acceptance/openstack/sharedfilesystems/v2/replicas_test.go @@ -13,8 +13,9 @@ import ( th "github.com/gophercloud/gophercloud/testhelper" ) +// 2.56 is required for a /v2/replicas/XXX URL support // otherwise we need to set "X-OpenStack-Manila-API-Experimental: true" -const replicasMicroversion = "2.60" +const replicasPathMicroversion = "2.56" func TestReplicaCreate(t *testing.T) { clients.RequireManilaReplicas(t) @@ -23,7 +24,7 @@ func TestReplicaCreate(t *testing.T) { if err != nil { t.Fatalf("Unable to create a shared file system client: %v", err) } - client.Microversion = replicasMicroversion + client.Microversion = replicasPathMicroversion share, err := CreateShare(t, client) if err != nil { @@ -60,7 +61,7 @@ func TestReplicaPromote(t *testing.T) { if err != nil { t.Fatalf("Unable to create a shared file system client: %v", err) } - client.Microversion = replicasMicroversion + client.Microversion = replicasPathMicroversion share, err := CreateShare(t, client) if err != nil { @@ -136,7 +137,7 @@ func TestReplicaExportLocations(t *testing.T) { if err != nil { t.Fatalf("Unable to create a shared file system client: %v", err) } - client.Microversion = replicasMicroversion + client.Microversion = replicasPathMicroversion share, err := CreateShare(t, client) if err != nil { @@ -203,7 +204,7 @@ func TestReplicaListDetail(t *testing.T) { if err != nil { t.Fatalf("Unable to create a shared file system client: %v", err) } - client.Microversion = replicasMicroversion + client.Microversion = replicasPathMicroversion share, err := CreateShare(t, client) if err != nil { @@ -236,7 +237,7 @@ func TestReplicaResetStatus(t *testing.T) { if err != nil { t.Fatalf("Unable to create a shared file system client: %v", err) } - client.Microversion = replicasMicroversion + client.Microversion = replicasPathMicroversion share, err := CreateShare(t, client) if err != nil { @@ -278,7 +279,7 @@ func TestReplicaForceDelete(t *testing.T) { if err != nil { t.Fatalf("Unable to create a shared file system client: %v", err) } - client.Microversion = replicasMicroversion + client.Microversion = replicasPathMicroversion share, err := CreateShare(t, client) if err != nil { diff --git a/acceptance/openstack/sharedfilesystems/v2/snapshots.go b/acceptance/openstack/sharedfilesystems/v2/snapshots.go index 62e607d229..e96d02b9b9 100644 --- a/acceptance/openstack/sharedfilesystems/v2/snapshots.go +++ b/acceptance/openstack/sharedfilesystems/v2/snapshots.go @@ -54,6 +54,9 @@ func ListSnapshots(t *testing.T, client *gophercloud.ServiceClient) ([]snapshots func DeleteSnapshot(t *testing.T, client *gophercloud.ServiceClient, snapshot *snapshots.Snapshot) { err := snapshots.Delete(client, snapshot.ID).ExtractErr() if err != nil { + if _, ok := err.(gophercloud.ErrDefault404); ok { + return + } t.Errorf("Unable to delete snapshot %s: %v", snapshot.ID, err) } diff --git a/acceptance/openstack/sharedfilesystems/v2/snapshots_test.go b/acceptance/openstack/sharedfilesystems/v2/snapshots_test.go index a30bad98bb..de83e41cd1 100644 --- a/acceptance/openstack/sharedfilesystems/v2/snapshots_test.go +++ b/acceptance/openstack/sharedfilesystems/v2/snapshots_test.go @@ -12,6 +12,10 @@ import ( th "github.com/gophercloud/gophercloud/testhelper" ) +// 2.7 is required for a /v2/snapshots/XXX/action URL support +// otherwise we need to set "X-OpenStack-Manila-API-Experimental: true" +const snapshotsPathMicroversion = "2.7" + func TestSnapshotCreate(t *testing.T) { client, err := clients.NewSharedFileSystemV2Client() if err != nil { @@ -119,3 +123,74 @@ func TestSnapshotListDetail(t *testing.T) { tools.PrintResource(t, &ss[i]) } } + +func TestSnapshotResetStatus(t *testing.T) { + client, err := clients.NewSharedFileSystemV2Client() + if err != nil { + t.Fatalf("Unable to create a shared file system client: %v", err) + } + client.Microversion = snapshotsPathMicroversion + + share, err := CreateShare(t, client) + if err != nil { + t.Fatalf("Unable to create a share: %v", err) + } + + defer DeleteShare(t, client, share) + + snapshot, err := CreateSnapshot(t, client, share.ID) + if err != nil { + t.Fatalf("Unable to create a snapshot: %v", err) + } + + defer DeleteSnapshot(t, client, snapshot) + + resetStatusOpts := &snapshots.ResetStatusOpts{ + Status: "error", + } + err = snapshots.ResetStatus(client, snapshot.ID, resetStatusOpts).ExtractErr() + if err != nil { + t.Fatalf("Unable to reset a snapshot status: %v", err) + } + + err = waitForSnapshotStatus(t, client, snapshot.ID, "error") + if err != nil { + t.Fatalf("Snapshot status error: %v", err) + } + + t.Logf("Snapshot %s status successfuly reset", snapshot.ID) +} + +func TestSnapshotForceDelete(t *testing.T) { + client, err := clients.NewSharedFileSystemV2Client() + if err != nil { + t.Fatalf("Unable to create a shared file system client: %v", err) + } + client.Microversion = snapshotsPathMicroversion + + share, err := CreateShare(t, client) + if err != nil { + t.Fatalf("Unable to create a share: %v", err) + } + + defer DeleteShare(t, client, share) + + snapshot, err := CreateSnapshot(t, client, share.ID) + if err != nil { + t.Fatalf("Unable to create a snapshot: %v", err) + } + + defer DeleteSnapshot(t, client, snapshot) + + err = snapshots.ForceDelete(client, snapshot.ID).ExtractErr() + if err != nil { + t.Fatalf("Unable to force delete a snapshot: %v", err) + } + + err = waitForSnapshotStatus(t, client, snapshot.ID, "deleted") + if err != nil { + t.Fatalf("Snapshot status error: %v", err) + } + + t.Logf("Snapshot %s was successfuly deleted", snapshot.ID) +} diff --git a/openstack/sharedfilesystems/v2/snapshots/requests.go b/openstack/sharedfilesystems/v2/snapshots/requests.go index 1ed6e8aef2..bbdde5eac1 100644 --- a/openstack/sharedfilesystems/v2/snapshots/requests.go +++ b/openstack/sharedfilesystems/v2/snapshots/requests.go @@ -163,3 +163,54 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } + +// ResetStatusOptsBuilder allows extensions to add additional parameters to the +// ResetStatus request. +type ResetStatusOptsBuilder interface { + ToSnapshotResetStatusMap() (map[string]interface{}, error) +} + +// ResetStatusOpts contains options for resetting a Snapshot status. +// For more information about these parameters, please, refer to the shared file systems API v2, +// Snapshot Actions, ResetStatus share documentation. +type ResetStatusOpts struct { + // Status is a snapshot status to reset to. Can be "available", "error", + // "creating", "deleting", "manage_starting", "manage_error", + // "unmanage_starting", "unmanage_error" or "error_deleting". + Status string `json:"status"` +} + +// ToSnapshotResetStatusMap assembles a request body based on the contents of a +// ResetStatusOpts. +func (opts ResetStatusOpts) ToSnapshotResetStatusMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "reset_status") +} + +// ResetStatus will reset the existing snapshot status. ResetStatusResult contains only the error. +// To extract it, call the ExtractErr method on the ResetStatusResult. +func ResetStatus(client *gophercloud.ServiceClient, id string, opts ResetStatusOptsBuilder) (r ResetStatusResult) { + b, err := opts.ToSnapshotResetStatusMap() + if err != nil { + r.Err = err + return + } + + resp, err := client.Post(resetStatusURL(client, id), b, nil, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// ForceDelete will delete the existing snapshot in any state. ForceDeleteResult contains only the error. +// To extract it, call the ExtractErr method on the ForceDeleteResult. +func ForceDelete(client *gophercloud.ServiceClient, id string) (r ForceDeleteResult) { + b := map[string]interface{}{ + "force_delete": nil, + } + resp, err := client.Post(forceDeleteURL(client, id), b, nil, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} diff --git a/openstack/sharedfilesystems/v2/snapshots/results.go b/openstack/sharedfilesystems/v2/snapshots/results.go index a3d45aaa94..44337b17a7 100644 --- a/openstack/sharedfilesystems/v2/snapshots/results.go +++ b/openstack/sharedfilesystems/v2/snapshots/results.go @@ -173,3 +173,13 @@ type GetResult struct { type UpdateResult struct { commonResult } + +// ResetStatusResult contains the response error from an ResetStatus request. +type ResetStatusResult struct { + gophercloud.ErrResult +} + +// ForceDeleteResult contains the response error from an ForceDelete request. +type ForceDeleteResult struct { + gophercloud.ErrResult +} diff --git a/openstack/sharedfilesystems/v2/snapshots/testing/fixtures.go b/openstack/sharedfilesystems/v2/snapshots/testing/fixtures.go index c02ef10c71..fb677918dd 100644 --- a/openstack/sharedfilesystems/v2/snapshots/testing/fixtures.go +++ b/openstack/sharedfilesystems/v2/snapshots/testing/fixtures.go @@ -204,3 +204,39 @@ func MockListDetailResponse(t *testing.T) { } }) } + +var resetStatusRequest = `{ + "reset_status": { + "status": "error" + } + }` + +// MockResetStatusResponse creates a mock reset status snapshot response +func MockResetStatusResponse(t *testing.T) { + th.Mux.HandleFunc(snapshotEndpoint+"/"+snapshotID+"/action", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestHeader(t, r, "Content-Type", "application/json") + th.TestHeader(t, r, "Accept", "application/json") + th.TestJSONRequest(t, r, resetStatusRequest) + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusAccepted) + }) +} + +var forceDeleteRequest = `{ + "force_delete": null + }` + +// MockForceDeleteResponse creates a mock force delete snapshot response +func MockForceDeleteResponse(t *testing.T) { + th.Mux.HandleFunc(snapshotEndpoint+"/"+snapshotID+"/action", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestHeader(t, r, "Content-Type", "application/json") + th.TestHeader(t, r, "Accept", "application/json") + th.TestJSONRequest(t, r, forceDeleteRequest) + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusAccepted) + }) +} diff --git a/openstack/sharedfilesystems/v2/snapshots/testing/request_test.go b/openstack/sharedfilesystems/v2/snapshots/testing/request_test.go index e210b4adc9..52f9c33a23 100644 --- a/openstack/sharedfilesystems/v2/snapshots/testing/request_test.go +++ b/openstack/sharedfilesystems/v2/snapshots/testing/request_test.go @@ -125,3 +125,27 @@ func TestListDetail(t *testing.T) { }, }) } + +func TestResetStatusSuccess(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockResetStatusResponse(t) + + c := client.ServiceClient() + + err := snapshots.ResetStatus(c, snapshotID, &snapshots.ResetStatusOpts{Status: "error"}).ExtractErr() + th.AssertNoErr(t, err) +} + +func TestForceDeleteSuccess(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockForceDeleteResponse(t) + + c := client.ServiceClient() + + err := snapshots.ForceDelete(c, snapshotID).ExtractErr() + th.AssertNoErr(t, err) +} diff --git a/openstack/sharedfilesystems/v2/snapshots/urls.go b/openstack/sharedfilesystems/v2/snapshots/urls.go index a07e3ec873..138d97f350 100644 --- a/openstack/sharedfilesystems/v2/snapshots/urls.go +++ b/openstack/sharedfilesystems/v2/snapshots/urls.go @@ -21,3 +21,11 @@ func getURL(c *gophercloud.ServiceClient, id string) string { func updateURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("snapshots", id) } + +func resetStatusURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("snapshots", id, "action") +} + +func forceDeleteURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("snapshots", id, "action") +} From b42636b9ced08f3651a36a33f1786fe517adb9e4 Mon Sep 17 00:00:00 2001 From: kayrus <kay.diam@gmail.com> Date: Mon, 17 Jul 2023 18:03:00 +0200 Subject: [PATCH 278/360] [cinder]: add reset and force delete actions to volumes and snapshots --- .../blockstorage/extensions/extensions.go | 19 +++ .../extensions/volumeactions_test.go | 17 +++ .../openstack/blockstorage/v3/blockstorage.go | 21 +++ .../blockstorage/v3/quotaset_test.go | 2 +- .../blockstorage/v3/snapshots_test.go | 124 ++++++++++++++++++ .../extensions/volumeactions/requests.go | 40 ++++++ .../extensions/volumeactions/results.go | 5 + .../volumeactions/testing/fixtures.go | 21 +++ .../volumeactions/testing/requests_test.go | 16 +++ .../blockstorage/v3/snapshots/requests.go | 87 ++++++++++++ .../blockstorage/v3/snapshots/results.go | 15 +++ .../v3/snapshots/testing/fixtures.go | 62 ++++++++- .../v3/snapshots/testing/requests_test.go | 37 ++++++ openstack/blockstorage/v3/snapshots/urls.go | 12 ++ .../remoteconsoles/testing/fixtures.go | 2 +- 15 files changed, 472 insertions(+), 8 deletions(-) diff --git a/acceptance/openstack/blockstorage/extensions/extensions.go b/acceptance/openstack/blockstorage/extensions/extensions.go index d15e4b652d..dfa357fdd3 100644 --- a/acceptance/openstack/blockstorage/extensions/extensions.go +++ b/acceptance/openstack/blockstorage/extensions/extensions.go @@ -314,6 +314,25 @@ func ChangeVolumeType(t *testing.T, client *gophercloud.ServiceClient, volume *v return nil } +// ResetVolumeStatus will reset the status of a volume. +func ResetVolumeStatus(t *testing.T, client *gophercloud.ServiceClient, volume *v3.Volume, status string) error { + t.Logf("Attempting to reset the status of volume %s from %s to %s", volume.ID, volume.Status, status) + + resetOpts := volumeactions.ResetStatusOpts{ + Status: status, + } + err := volumeactions.ResetStatus(client, volume.ID, resetOpts).ExtractErr() + if err != nil { + return err + } + + if err := volumes.WaitForStatus(client, volume.ID, status, 60); err != nil { + return err + } + + return nil +} + // ReImage will re-image a volume func ReImage(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume, imageID string) error { t.Logf("Attempting to re-image volume %s", volume.ID) diff --git a/acceptance/openstack/blockstorage/extensions/volumeactions_test.go b/acceptance/openstack/blockstorage/extensions/volumeactions_test.go index 3c69d17a46..98541419df 100644 --- a/acceptance/openstack/blockstorage/extensions/volumeactions_test.go +++ b/acceptance/openstack/blockstorage/extensions/volumeactions_test.go @@ -145,6 +145,23 @@ func TestVolumeActionsChangeType(t *testing.T) { tools.PrintResource(t, newVolume) } +func TestVolumeActionsResetStatus(t *testing.T) { + client, err := clients.NewBlockStorageV3Client() + th.AssertNoErr(t, err) + + volume, err := blockstorageV3.CreateVolume(t, client) + th.AssertNoErr(t, err) + defer blockstorageV3.DeleteVolume(t, client, volume) + + tools.PrintResource(t, volume) + + err = ResetVolumeStatus(t, client, volume, "error") + th.AssertNoErr(t, err) + + err = ResetVolumeStatus(t, client, volume, "available") + th.AssertNoErr(t, err) +} + func TestVolumeActionsReImage(t *testing.T) { clients.SkipReleasesBelow(t, "stable/yoga") diff --git a/acceptance/openstack/blockstorage/v3/blockstorage.go b/acceptance/openstack/blockstorage/v3/blockstorage.go index 1645f327b6..ad6518b7de 100644 --- a/acceptance/openstack/blockstorage/v3/blockstorage.go +++ b/acceptance/openstack/blockstorage/v3/blockstorage.go @@ -38,6 +38,11 @@ func CreateSnapshot(t *testing.T, client *gophercloud.ServiceClient, volume *vol return snapshot, err } + snapshot, err = snapshots.Get(client, snapshot.ID).Extract() + if err != nil { + return snapshot, err + } + tools.PrintResource(t, snapshot) th.AssertEquals(t, snapshot.Name, snapshotName) th.AssertEquals(t, snapshot.VolumeID, volume.ID) @@ -70,6 +75,11 @@ func CreateVolume(t *testing.T, client *gophercloud.ServiceClient) (*volumes.Vol return volume, err } + volume, err = volumes.Get(client, volume.ID).Extract() + if err != nil { + return volume, err + } + tools.PrintResource(t, volume) th.AssertEquals(t, volume.Name, volumeName) th.AssertEquals(t, volume.Description, volumeDescription) @@ -105,6 +115,11 @@ func CreateVolumeWithType(t *testing.T, client *gophercloud.ServiceClient, vt *v return volume, err } + volume, err = volumes.Get(client, volume.ID).Extract() + if err != nil { + return volume, err + } + tools.PrintResource(t, volume) th.AssertEquals(t, volume.Name, volumeName) th.AssertEquals(t, volume.Description, volumeDescription) @@ -243,6 +258,9 @@ func CreatePrivateVolumeType(t *testing.T, client *gophercloud.ServiceClient) (* func DeleteSnapshot(t *testing.T, client *gophercloud.ServiceClient, snapshot *snapshots.Snapshot) { err := snapshots.Delete(client, snapshot.ID).ExtractErr() if err != nil { + if _, ok := err.(gophercloud.ErrDefault404); ok { + return + } t.Fatalf("Unable to delete snapshot %s: %+v", snapshot.ID, err) } @@ -270,6 +288,9 @@ func DeleteVolume(t *testing.T, client *gophercloud.ServiceClient, volume *volum err := volumes.Delete(client, volume.ID, volumes.DeleteOpts{}).ExtractErr() if err != nil { + if _, ok := err.(gophercloud.ErrDefault404); ok { + return + } t.Fatalf("Unable to delete volume %s: %v", volume.ID, err) } diff --git a/acceptance/openstack/blockstorage/v3/quotaset_test.go b/acceptance/openstack/blockstorage/v3/quotaset_test.go index bffe0793d1..5a9d702954 100644 --- a/acceptance/openstack/blockstorage/v3/quotaset_test.go +++ b/acceptance/openstack/blockstorage/v3/quotaset_test.go @@ -118,7 +118,7 @@ func TestQuotasetUpdate(t *testing.T) { // test that resultQuotas.Extra is populated with the 3 new quota types // for the new volumeType foo, don't take into account other volume types count := 0 - for k, _ := range resultQuotas.Extra { + for k := range resultQuotas.Extra { tools.PrintResource(t, k) switch k { case diff --git a/acceptance/openstack/blockstorage/v3/snapshots_test.go b/acceptance/openstack/blockstorage/v3/snapshots_test.go index 06ad8e8ba0..3c3d08ef4c 100644 --- a/acceptance/openstack/blockstorage/v3/snapshots_test.go +++ b/acceptance/openstack/blockstorage/v3/snapshots_test.go @@ -4,8 +4,10 @@ package v3 import ( + "fmt" "testing" + "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/snapshots" @@ -73,3 +75,125 @@ func TestSnapshots(t *testing.T) { th.AssertNoErr(t, err) } + +func TestSnapshotsResetStatus(t *testing.T) { + clients.RequireLong(t) + + client, err := clients.NewBlockStorageV3Client() + th.AssertNoErr(t, err) + + volume1, err := CreateVolume(t, client) + th.AssertNoErr(t, err) + defer DeleteVolume(t, client, volume1) + + snapshot1, err := CreateSnapshot(t, client, volume1) + th.AssertNoErr(t, err) + defer DeleteSnapshot(t, client, snapshot1) + + // Reset snapshot status to error + resetOpts := snapshots.ResetStatusOpts{ + Status: "error", + } + t.Logf("Attempting to reset snapshot status to %s", resetOpts.Status) + err = snapshots.ResetStatus(client, snapshot1.ID, resetOpts).ExtractErr() + th.AssertNoErr(t, err) + + snapshot, err := snapshots.Get(client, snapshot1.ID).Extract() + th.AssertNoErr(t, err) + + if snapshot.Status != resetOpts.Status { + th.AssertNoErr(t, fmt.Errorf("unexpected %q snapshot status", snapshot.Status)) + } + + // Reset snapshot status to available + resetOpts = snapshots.ResetStatusOpts{ + Status: "available", + } + t.Logf("Attempting to reset snapshot status to %s", resetOpts.Status) + err = snapshots.ResetStatus(client, snapshot1.ID, resetOpts).ExtractErr() + th.AssertNoErr(t, err) + + snapshot, err = snapshots.Get(client, snapshot1.ID).Extract() + th.AssertNoErr(t, err) + + if snapshot.Status != resetOpts.Status { + th.AssertNoErr(t, fmt.Errorf("unexpected %q snapshot status", snapshot.Status)) + } +} + +func TestSnapshotsUpdateStatus(t *testing.T) { + clients.RequireLong(t) + + client, err := clients.NewBlockStorageV3Client() + th.AssertNoErr(t, err) + + volume1, err := CreateVolume(t, client) + th.AssertNoErr(t, err) + defer DeleteVolume(t, client, volume1) + + snapshot1, err := CreateSnapshot(t, client, volume1) + th.AssertNoErr(t, err) + defer DeleteSnapshot(t, client, snapshot1) + + // Update snapshot status to error + resetOpts := snapshots.ResetStatusOpts{ + Status: "creating", + } + t.Logf("Attempting to update snapshot status to %s", resetOpts.Status) + err = snapshots.ResetStatus(client, snapshot1.ID, resetOpts).ExtractErr() + th.AssertNoErr(t, err) + + snapshot, err := snapshots.Get(client, snapshot1.ID).Extract() + th.AssertNoErr(t, err) + + if snapshot.Status != resetOpts.Status { + th.AssertNoErr(t, fmt.Errorf("unexpected %q snapshot status", snapshot.Status)) + } + + // Update snapshot status to available + updateOpts := snapshots.UpdateStatusOpts{ + Status: "available", + } + t.Logf("Attempting to update snapshot status to %s", updateOpts.Status) + err = snapshots.UpdateStatus(client, snapshot1.ID, updateOpts).ExtractErr() + th.AssertNoErr(t, err) + + snapshot, err = snapshots.Get(client, snapshot1.ID).Extract() + th.AssertNoErr(t, err) + + if snapshot.Status != updateOpts.Status { + th.AssertNoErr(t, fmt.Errorf("unexpected %q snapshot status", snapshot.Status)) + } +} + +func TestSnapshotsForceDelete(t *testing.T) { + clients.RequireLong(t) + + client, err := clients.NewBlockStorageV3Client() + th.AssertNoErr(t, err) + + volume, err := CreateVolume(t, client) + th.AssertNoErr(t, err) + defer DeleteVolume(t, client, volume) + + snapshot, err := CreateSnapshot(t, client, volume) + th.AssertNoErr(t, err) + defer DeleteSnapshot(t, client, snapshot) + + // Force delete snapshot + t.Logf("Attempting to force delete %s snapshot", snapshot.ID) + err = snapshots.ForceDelete(client, snapshot.ID).ExtractErr() + th.AssertNoErr(t, err) + + err = tools.WaitFor(func() (bool, error) { + _, err := snapshots.Get(client, snapshot.ID).Extract() + if err != nil { + if _, ok := err.(gophercloud.ErrDefault404); ok { + return true, nil + } + } + + return false, nil + }) + th.AssertNoErr(t, err) +} diff --git a/openstack/blockstorage/extensions/volumeactions/requests.go b/openstack/blockstorage/extensions/volumeactions/requests.go index 09dfb9ed2f..03fb724a9f 100644 --- a/openstack/blockstorage/extensions/volumeactions/requests.go +++ b/openstack/blockstorage/extensions/volumeactions/requests.go @@ -418,3 +418,43 @@ func ReImage(client *gophercloud.ServiceClient, id string, opts ReImageOpts) (r _, 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]interface{}, 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]interface{}, 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(client *gophercloud.ServiceClient, id string, opts ResetStatusOptsBuilder) (r ResetStatusResult) { + b, err := opts.ToResetStatusMap() + 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 +} diff --git a/openstack/blockstorage/extensions/volumeactions/results.go b/openstack/blockstorage/extensions/volumeactions/results.go index 95b5bac1cb..34f64e18e8 100644 --- a/openstack/blockstorage/extensions/volumeactions/results.go +++ b/openstack/blockstorage/extensions/volumeactions/results.go @@ -219,3 +219,8 @@ type ChangeTypeResult struct { type ReImageResult struct { gophercloud.ErrResult } + +// ResetStatusResult contains the response error from a ResetStatus request. +type ResetStatusResult struct { + gophercloud.ErrResult +} diff --git a/openstack/blockstorage/extensions/volumeactions/testing/fixtures.go b/openstack/blockstorage/extensions/volumeactions/testing/fixtures.go index 378a120bc6..eef61d477a 100644 --- a/openstack/blockstorage/extensions/volumeactions/testing/fixtures.go +++ b/openstack/blockstorage/extensions/volumeactions/testing/fixtures.go @@ -370,3 +370,24 @@ func MockChangeTypeResponse(t *testing.T) { fmt.Fprintf(w, `{}`) }) } + +func MockResetStatusResponse(t *testing.T) { + th.Mux.HandleFunc("/volumes/cd281d77-8217-4830-be95-9528227c105c/action", + func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestHeader(t, r, "Content-Type", "application/json") + th.TestJSONRequest(t, r, ` +{ + "os-reset_status": + { + "status": "error", + "attach_status": "detached", + "migration_status": "migrating" + } +} + `) + + w.WriteHeader(http.StatusAccepted) + }) +} diff --git a/openstack/blockstorage/extensions/volumeactions/testing/requests_test.go b/openstack/blockstorage/extensions/volumeactions/testing/requests_test.go index 2191a8a788..bb5d02e926 100644 --- a/openstack/blockstorage/extensions/volumeactions/testing/requests_test.go +++ b/openstack/blockstorage/extensions/volumeactions/testing/requests_test.go @@ -224,3 +224,19 @@ func TestChangeType(t *testing.T) { err := volumeactions.ChangeType(client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options).ExtractErr() th.AssertNoErr(t, err) } + +func TestResetStatus(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockResetStatusResponse(t) + + options := &volumeactions.ResetStatusOpts{ + Status: "error", + AttachStatus: "detached", + MigrationStatus: "migrating", + } + + err := volumeactions.ResetStatus(client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options).ExtractErr() + th.AssertNoErr(t, err) +} diff --git a/openstack/blockstorage/v3/snapshots/requests.go b/openstack/blockstorage/v3/snapshots/requests.go index 7dcfed7946..9bbec339ec 100644 --- a/openstack/blockstorage/v3/snapshots/requests.go +++ b/openstack/blockstorage/v3/snapshots/requests.go @@ -189,3 +189,90 @@ func UpdateMetadata(client *gophercloud.ServiceClient, id string, opts UpdateMet _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } + +// ResetStatusOptsBuilder allows extensions to add additional parameters to the +// ResetStatus request. +type ResetStatusOptsBuilder interface { + ToSnapshotResetStatusMap() (map[string]interface{}, error) +} + +// ResetStatusOpts contains options for resetting a Snapshot status. +// For more information about these parameters, please, refer to the Block Storage API V3, +// Snapshot Actions, ResetStatus snapshot documentation. +type ResetStatusOpts struct { + // Status is a snapshot status to reset to. + Status string `json:"status"` +} + +// ToSnapshotResetStatusMap assembles a request body based on the contents of a +// ResetStatusOpts. +func (opts ResetStatusOpts) ToSnapshotResetStatusMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "os-reset_status") +} + +// ResetStatus will reset the existing snapshot status. ResetStatusResult contains only the error. +// To extract it, call the ExtractErr method on the ResetStatusResult. +func ResetStatus(client *gophercloud.ServiceClient, id string, opts ResetStatusOptsBuilder) (r ResetStatusResult) { + b, err := opts.ToSnapshotResetStatusMap() + if err != nil { + r.Err = err + return + } + + resp, err := client.Post(resetStatusURL(client, id), b, nil, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// UpdateStatusOptsBuilder allows extensions to add additional parameters to the +// UpdateStatus request. +type UpdateStatusOptsBuilder interface { + ToSnapshotUpdateStatusMap() (map[string]interface{}, error) +} + +// UpdateStatusOpts contains options for resetting a Snapshot status. +// For more information about these parameters, please, refer to the Block Storage API V3, +// Snapshot Actions, UpdateStatus snapshot documentation. +type UpdateStatusOpts struct { + // Status is a snapshot status to update to. + Status string `json:"status"` + // A progress percentage value for snapshot build progress. + Progress string `json:"progress,omitempty"` +} + +// ToSnapshotUpdateStatusMap assembles a request body based on the contents of a +// UpdateStatusOpts. +func (opts UpdateStatusOpts) ToSnapshotUpdateStatusMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "os-update_snapshot_status") +} + +// UpdateStatus will reset the existing snapshot status. UpdateStatusResult contains only the error. +// To extract it, call the ExtractErr method on the UpdateStatusResult. +func UpdateStatus(client *gophercloud.ServiceClient, id string, opts UpdateStatusOptsBuilder) (r UpdateStatusResult) { + b, err := opts.ToSnapshotUpdateStatusMap() + if err != nil { + r.Err = err + return + } + + resp, err := client.Post(resetStatusURL(client, id), b, nil, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// ForceDelete will delete the existing snapshot in any state. ForceDeleteResult contains only the error. +// To extract it, call the ExtractErr method on the ForceDeleteResult. +func ForceDelete(client *gophercloud.ServiceClient, id string) (r ForceDeleteResult) { + b := map[string]interface{}{ + "os-force_delete": struct{}{}, + } + resp, err := client.Post(forceDeleteURL(client, id), b, nil, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} diff --git a/openstack/blockstorage/v3/snapshots/results.go b/openstack/blockstorage/v3/snapshots/results.go index d058b22172..23c7a506d8 100644 --- a/openstack/blockstorage/v3/snapshots/results.go +++ b/openstack/blockstorage/v3/snapshots/results.go @@ -141,3 +141,18 @@ func (r commonResult) Extract() (*Snapshot, error) { err := r.ExtractInto(&s) return s.Snapshot, err } + +// ResetStatusResult contains the response error from a ResetStatus request. +type ResetStatusResult struct { + gophercloud.ErrResult +} + +// UpdateStatusResult contains the response error from an UpdateStatus request. +type UpdateStatusResult struct { + gophercloud.ErrResult +} + +// ForceDeleteResult contains the response error from a ForceDelete request. +type ForceDeleteResult struct { + gophercloud.ErrResult +} diff --git a/openstack/blockstorage/v3/snapshots/testing/fixtures.go b/openstack/blockstorage/v3/snapshots/testing/fixtures.go index 3ae3bb86d2..be48c50048 100644 --- a/openstack/blockstorage/v3/snapshots/testing/fixtures.go +++ b/openstack/blockstorage/v3/snapshots/testing/fixtures.go @@ -9,7 +9,7 @@ import ( fake "github.com/gophercloud/gophercloud/testhelper/client" ) -// MockListResponse provides mock responce for list snapshot API call +// MockListResponse provides mock response for list snapshot API call func MockListResponse(t *testing.T) { th.Mux.HandleFunc("/snapshots", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") @@ -59,7 +59,7 @@ func MockListResponse(t *testing.T) { }) } -// MockGetResponse provides mock responce for get snapshot API call +// MockGetResponse provides mock response for get snapshot API call func MockGetResponse(t *testing.T) { th.Mux.HandleFunc("/snapshots/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") @@ -83,7 +83,7 @@ func MockGetResponse(t *testing.T) { }) } -// MockCreateResponse provides mock responce for create snapshot API call +// MockCreateResponse provides mock response for create snapshot API call func MockCreateResponse(t *testing.T) { th.Mux.HandleFunc("/snapshots", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") @@ -119,7 +119,7 @@ func MockCreateResponse(t *testing.T) { }) } -// MockUpdateMetadataResponse provides mock responce for update metadata snapshot API call +// MockUpdateMetadataResponse provides mock response for update metadata snapshot API call func MockUpdateMetadataResponse(t *testing.T) { th.Mux.HandleFunc("/snapshots/123/metadata", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") @@ -143,7 +143,7 @@ func MockUpdateMetadataResponse(t *testing.T) { }) } -// MockDeleteResponse provides mock responce for delete snapshot API call +// MockDeleteResponse provides mock response for delete snapshot API call func MockDeleteResponse(t *testing.T) { th.Mux.HandleFunc("/snapshots/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") @@ -152,7 +152,7 @@ func MockDeleteResponse(t *testing.T) { }) } -// MockUpdateResponse provides mock responce for update snapshot API call +// MockUpdateResponse provides mock response for update snapshot API call func MockUpdateResponse(t *testing.T) { th.Mux.HandleFunc("/snapshots/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") @@ -176,3 +176,53 @@ func MockUpdateResponse(t *testing.T) { `) }) } + +// MockResetStatusResponse provides mock response for reset snapshot status API call +func MockResetStatusResponse(t *testing.T) { + th.Mux.HandleFunc("/snapshots/d32019d3-bc6e-4319-9c1d-6722fc136a22/action", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestHeader(t, r, "Content-Type", "application/json") + th.TestJSONRequest(t, r, ` +{ + "os-reset_status": { + "status": "error" + } +} + `) + w.WriteHeader(http.StatusAccepted) + }) +} + +// MockUpdateStatusResponse provides mock response for update snapshot status API call +func MockUpdateStatusResponse(t *testing.T) { + th.Mux.HandleFunc("/snapshots/d32019d3-bc6e-4319-9c1d-6722fc136a22/action", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestHeader(t, r, "Content-Type", "application/json") + th.TestJSONRequest(t, r, ` +{ + "os-update_snapshot_status": { + "status": "error", + "progress": "80%" + } +} + `) + w.WriteHeader(http.StatusAccepted) + }) +} + +// MockForceDeleteResponse provides mock response for force delete snapshot API call +func MockForceDeleteResponse(t *testing.T) { + th.Mux.HandleFunc("/snapshots/d32019d3-bc6e-4319-9c1d-6722fc136a22/action", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestHeader(t, r, "Content-Type", "application/json") + th.TestJSONRequest(t, r, ` +{ + "os-force_delete": {} +} + `) + w.WriteHeader(http.StatusAccepted) + }) +} diff --git a/openstack/blockstorage/v3/snapshots/testing/requests_test.go b/openstack/blockstorage/v3/snapshots/testing/requests_test.go index a787928ba9..862b25e010 100644 --- a/openstack/blockstorage/v3/snapshots/testing/requests_test.go +++ b/openstack/blockstorage/v3/snapshots/testing/requests_test.go @@ -129,3 +129,40 @@ func TestUpdate(t *testing.T) { th.CheckEquals(t, "snapshot-002", v.Name) th.CheckEquals(t, "Daily backup 002", v.Description) } + +func TestResetStatus(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockResetStatusResponse(t) + + opts := &snapshots.ResetStatusOpts{ + Status: "error", + } + res := snapshots.ResetStatus(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22", opts) + th.AssertNoErr(t, res.Err) +} + +func TestUpdateStatus(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockUpdateStatusResponse(t) + + opts := &snapshots.UpdateStatusOpts{ + Status: "error", + Progress: "80%", + } + res := snapshots.UpdateStatus(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22", opts) + th.AssertNoErr(t, res.Err) +} + +func TestForceDelete(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockForceDeleteResponse(t) + + res := snapshots.ForceDelete(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22") + th.AssertNoErr(t, res.Err) +} diff --git a/openstack/blockstorage/v3/snapshots/urls.go b/openstack/blockstorage/v3/snapshots/urls.go index d0bcbfb98c..605b3cf5ec 100644 --- a/openstack/blockstorage/v3/snapshots/urls.go +++ b/openstack/blockstorage/v3/snapshots/urls.go @@ -29,3 +29,15 @@ func metadataURL(c *gophercloud.ServiceClient, id string) string { func updateMetadataURL(c *gophercloud.ServiceClient, id string) string { return metadataURL(c, id) } + +func resetStatusURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("snapshots", id, "action") +} + +func updateStatusURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("snapshots", id, "action") +} + +func forceDeleteURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("snapshots", id, "action") +} diff --git a/openstack/compute/v2/extensions/remoteconsoles/testing/fixtures.go b/openstack/compute/v2/extensions/remoteconsoles/testing/fixtures.go index 9644a4895e..53b9f5b88b 100644 --- a/openstack/compute/v2/extensions/remoteconsoles/testing/fixtures.go +++ b/openstack/compute/v2/extensions/remoteconsoles/testing/fixtures.go @@ -10,7 +10,7 @@ const RemoteConsoleCreateRequest = ` } ` -// RemoteConsoleCreateResult represents a raw server responce to the RemoteConsoleCreateRequest. +// RemoteConsoleCreateResult represents a raw server response to the RemoteConsoleCreateRequest. const RemoteConsoleCreateResult = ` { "remote_console": { From 705c6489ebb6244ac33bdda1be45254d32244c43 Mon Sep 17 00:00:00 2001 From: Pierre Prinetti <pierreprinetti@redhat.com> Date: Fri, 2 Dec 2022 12:41:12 +0100 Subject: [PATCH 279/360] orchestration: Explicit error in optionsmap creation Add verbosity to the error signaling a failure in the creation of the optionsmap. --- openstack/orchestration/v1/stacks/requests.go | 3 ++- openstack/orchestration/v1/stacks/testing/requests_test.go | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/openstack/orchestration/v1/stacks/requests.go b/openstack/orchestration/v1/stacks/requests.go index 620e7576dc..0bd16543b9 100644 --- a/openstack/orchestration/v1/stacks/requests.go +++ b/openstack/orchestration/v1/stacks/requests.go @@ -1,6 +1,7 @@ package stacks import ( + "fmt" "strings" "github.com/gophercloud/gophercloud" @@ -89,7 +90,7 @@ func (opts CreateOpts) ToStackCreateMap() (map[string]interface{}, error) { func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToStackCreateMap() if err != nil { - r.Err = err + r.Err = fmt.Errorf("error creating the options map: %w", err) return } resp, err := c.Post(createURL(c), b, &r.Body, nil) diff --git a/openstack/orchestration/v1/stacks/testing/requests_test.go b/openstack/orchestration/v1/stacks/testing/requests_test.go index 6f0eb3a8e9..9f0932a4d2 100644 --- a/openstack/orchestration/v1/stacks/testing/requests_test.go +++ b/openstack/orchestration/v1/stacks/testing/requests_test.go @@ -59,7 +59,7 @@ func TestCreateStackMissingRequiredInOpts(t *testing.T) { DisableRollback: gophercloud.Disabled, } r := stacks.Create(fake.ServiceClient(), createOpts) - th.AssertEquals(t, "Missing input for argument [Name]", r.Err.Error()) + th.AssertEquals(t, "error creating the options map: Missing input for argument [Name]", r.Err.Error()) } func TestAdoptStack(t *testing.T) { From 1f3de5382281c6b852a5b41167108c35c91fa8e6 Mon Sep 17 00:00:00 2001 From: Duc Truong <duct@nvidia.com> Date: Mon, 7 Aug 2023 14:01:40 -0700 Subject: [PATCH 280/360] Add conductor API to Baremetal V1 --- .../openstack/baremetal/v1/conductors_test.go | 32 ++++ openstack/baremetal/v1/conductors/doc.go | 46 +++++ openstack/baremetal/v1/conductors/requests.go | 72 +++++++ openstack/baremetal/v1/conductors/results.go | 93 +++++++++ .../baremetal/v1/conductors/testing/doc.go | 2 + .../v1/conductors/testing/fixtures.go | 181 ++++++++++++++++++ .../v1/conductors/testing/requests_test.go | 106 ++++++++++ openstack/baremetal/v1/conductors/urls.go | 11 ++ 8 files changed, 543 insertions(+) create mode 100644 acceptance/openstack/baremetal/v1/conductors_test.go create mode 100644 openstack/baremetal/v1/conductors/doc.go create mode 100644 openstack/baremetal/v1/conductors/requests.go create mode 100644 openstack/baremetal/v1/conductors/results.go create mode 100644 openstack/baremetal/v1/conductors/testing/doc.go create mode 100644 openstack/baremetal/v1/conductors/testing/fixtures.go create mode 100644 openstack/baremetal/v1/conductors/testing/requests_test.go create mode 100644 openstack/baremetal/v1/conductors/urls.go diff --git a/acceptance/openstack/baremetal/v1/conductors_test.go b/acceptance/openstack/baremetal/v1/conductors_test.go new file mode 100644 index 0000000000..f587d6fc3f --- /dev/null +++ b/acceptance/openstack/baremetal/v1/conductors_test.go @@ -0,0 +1,32 @@ +//go:build acceptance || baremetal || ports +// +build acceptance baremetal ports + +package v1 + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/openstack/baremetal/v1/conductors" + "github.com/gophercloud/gophercloud/pagination" + + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestConductorsList(t *testing.T) { + clients.RequireLong(t) + + client, err := clients.NewBareMetalV1Client() + th.AssertNoErr(t, err) + client.Microversion = "1.53" + + err = conductors.List(client, conductors.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + _, err := conductors.ExtractConductors(page) + if err != nil { + return false, err + } + + return false, nil + }) + th.AssertNoErr(t, err) +} diff --git a/openstack/baremetal/v1/conductors/doc.go b/openstack/baremetal/v1/conductors/doc.go new file mode 100644 index 0000000000..904910044c --- /dev/null +++ b/openstack/baremetal/v1/conductors/doc.go @@ -0,0 +1,46 @@ +/* +Package conductors provides information and interaction with the conductors API +resource in the OpenStack Bare Metal service. + +Example to List Conductors with Detail + + conductors.List(client, conductors.ListOpts{Detail: true}).EachPage(func(page pagination.Page) (bool, error) { + conductorList, err := conductors.ExtractConductors(page) + if err != nil { + return false, err + } + + for _, n := range conductorList { + // Do something + } + + return true, nil + }) + +Example to List Conductors + + listOpts := conductors.ListOpts{ + Fields: []string{"hostname"}, + } + + conductors.List(client, listOpts).EachPage(func(page pagination.Page) (bool, error) { + conductorList, err := conductors.ExtractConductors(page) + if err != nil { + return false, err + } + + for _, n := range conductorList { + // Do something + } + + return true, nil + }) + +Example to Get Conductor + + showConductor, err := conductors.Get(client, "compute2.localdomain").Extract() + if err != nil { + panic(err) + } +*/ +package conductors diff --git a/openstack/baremetal/v1/conductors/requests.go b/openstack/baremetal/v1/conductors/requests.go new file mode 100644 index 0000000000..f5bc63d63e --- /dev/null +++ b/openstack/baremetal/v1/conductors/requests.go @@ -0,0 +1,72 @@ +package conductors + +import ( + "fmt" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// ListOptsBuilder allows extensions to add additional parameters to the +// List request. +type ListOptsBuilder interface { + ToConductorListQuery() (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 conductor attributes you want to see returned. Marker and Limit are used +// for pagination. +type ListOpts struct { + // One or more fields to be returned in the response. + Fields []string `q:"fields"` + + // Requests a page size of items. + Limit int `q:"limit"` + + // The ID of the last-seen item. + Marker string `q:"marker"` + + // Sorts the response by the requested sort direction. + SortDir string `q:"sort_dir"` + + // Sorts the response by the this attribute value. + SortKey string `q:"sort_key"` + + // Provide additional information for the BIOS Settings + Detail bool `q:"detail"` +} + +// ToConductorListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToConductorListQuery() (string, error) { + if opts.Detail == true && len(opts.Fields) > 0 { + return "", fmt.Errorf("cannot have both fields and detail options for conductors") + } + + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// List makes a request against the API to list conductors accessible to you. +func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := listURL(client) + if opts != nil { + query, err := opts.ToConductorListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return ConductorPage{pagination.LinkedPageBase{PageResult: r}} + }) +} + +// Get requests details on a single conductor by hostname +func Get(client *gophercloud.ServiceClient, name string) (r GetResult) { + resp, err := client.Get(getURL(client, name), &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} diff --git a/openstack/baremetal/v1/conductors/results.go b/openstack/baremetal/v1/conductors/results.go new file mode 100644 index 0000000000..9dd4e963c3 --- /dev/null +++ b/openstack/baremetal/v1/conductors/results.go @@ -0,0 +1,93 @@ +package conductors + +import ( + "time" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +type conductorResult struct { + gophercloud.Result +} + +// Extract interprets any conductorResult as a Conductor, if possible. +func (r conductorResult) Extract() (*Conductor, error) { + var s Conductor + err := r.ExtractInto(&s) + return &s, err +} + +func (r conductorResult) ExtractInto(v interface{}) error { + return r.Result.ExtractIntoStructPtr(v, "") +} + +func ExtractConductorInto(r pagination.Page, v interface{}) error { + return r.(ConductorPage).Result.ExtractIntoSlicePtr(v, "conductors") +} + +// Conductor represents a conductor in the OpenStack Bare Metal API. +type Conductor struct { + // Whether or not this Conductor is alive or not + Alive bool `json:"alive"` + + // Hostname of this conductor + Hostname string `json:"hostname"` + + // Array of drivers for this conductor. + Drivers []string `json:"drivers"` + + // Conductor group for a conductor. Case-insensitive string up to 255 characters, containing a-z, 0-9, _, -, and .. + ConductorGroup string `json:"conductor_group"` + + // The UTC date and time when the resource was created, ISO 8601 format. + CreatedAt time.Time `json:"created_at"` + + // The UTC date and time when the resource was updated, ISO 8601 format. May be “null”. + UpdatedAt time.Time `json:"updated_at"` +} + +// ConductorPage abstracts the raw results of making a List() request against +// the API. As OpenStack extensions may freely alter the response bodies of +// structures returned to the client, you may only safely access the data +// provided through the ExtractConductor call. +type ConductorPage struct { + pagination.LinkedPageBase +} + +// IsEmpty returns true if a page contains no conductor results. +func (r ConductorPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + + s, err := ExtractConductors(r) + return len(s) == 0, err +} + +// NextPageURL uses the response's embedded link reference to navigate to the +// next page of results. +func (r ConductorPage) NextPageURL() (string, error) { + var s struct { + Links []gophercloud.Link `json:"conductor_links"` + } + err := r.ExtractInto(&s) + if err != nil { + return "", err + } + return gophercloud.ExtractNextURL(s.Links) +} + +// ExtractConductors interprets the results of a single page from a List() call, +// producing a slice of Conductor entities. +func ExtractConductors(r pagination.Page) ([]Conductor, error) { + var s []Conductor + err := ExtractConductorInto(r, &s) + return s, err +} + +// GetResult is the response from a Get operation. Call its Extract +// method to interpret it as a Conductor. +type GetResult struct { + conductorResult +} diff --git a/openstack/baremetal/v1/conductors/testing/doc.go b/openstack/baremetal/v1/conductors/testing/doc.go new file mode 100644 index 0000000000..9cc2466b89 --- /dev/null +++ b/openstack/baremetal/v1/conductors/testing/doc.go @@ -0,0 +1,2 @@ +// conductors unit tests +package testing diff --git a/openstack/baremetal/v1/conductors/testing/fixtures.go b/openstack/baremetal/v1/conductors/testing/fixtures.go new file mode 100644 index 0000000000..02e671aa28 --- /dev/null +++ b/openstack/baremetal/v1/conductors/testing/fixtures.go @@ -0,0 +1,181 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + "time" + + "github.com/gophercloud/gophercloud/openstack/baremetal/v1/conductors" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +// ConductorListBody contains the canned body of a conductor.List response, without detail. +const ConductorListBody = ` + { + "conductors": [ + { + "hostname": "compute1.localdomain", + "conductor_group": "", + "links": [ + { + "href": "http://127.0.0.1:6385/v1/conductors/compute1.localdomain", + "rel": "self" + }, + { + "href": "http://127.0.0.1:6385/conductors/compute1.localdomain", + "rel": "bookmark" + } + ], + "alive": false + }, + { + "hostname": "compute2.localdomain", + "conductor_group": "", + "links": [ + { + "href": "http://127.0.0.1:6385/v1/conductors/compute2.localdomain", + "rel": "self" + }, + { + "href": "http://127.0.0.1:6385/conductors/compute2.localdomain", + "rel": "bookmark" + } + ], + "alive": true + } + ] + } +` + +// ConductorListDetailBody contains the canned body of a conductor.ListDetail response. +const ConductorListDetailBody = ` +{ + "conductors": [ + { + "links": [ + { + "href": "http://127.0.0.1:6385/v1/conductors/compute1.localdomain", + "rel": "self" + }, + { + "href": "http://127.0.0.1:6385/conductors/compute1.localdomain", + "rel": "bookmark" + } + ], + "created_at": "2018-08-07T08:39:21+00:00", + "hostname": "compute1.localdomain", + "conductor_group": "", + "updated_at": "2018-11-30T07:07:23+00:00", + "alive": false, + "drivers": [ + "ipmi" + ] + }, + { + "links": [ + { + "href": "http://127.0.0.1:6385/v1/conductors/compute2.localdomain", + "rel": "self" + }, + { + "href": "http://127.0.0.1:6385/conductors/compute2.localdomain", + "rel": "bookmark" + } + ], + "created_at": "2018-12-05T07:03:19+00:00", + "hostname": "compute2.localdomain", + "conductor_group": "", + "updated_at": "2018-12-05T07:03:21+00:00", + "alive": true, + "drivers": [ + "ipmi" + ] + } + ] +} +` + +// SingleConductorBody is the canned body of a Get request on an existing conductor. +const SingleConductorBody = ` +{ + "links": [ + { + "href": "http://127.0.0.1:6385/v1/conductors/compute2.localdomain", + "rel": "self" + }, + { + "href": "http://127.0.0.1:6385/conductors/compute2.localdomain", + "rel": "bookmark" + } + ], + "created_at": "2018-12-05T07:03:19+00:00", + "hostname": "compute2.localdomain", + "conductor_group": "", + "updated_at": "2018-12-05T07:03:21+00:00", + "alive": true, + "drivers": [ + "ipmi" + ] +} +` + +var ( + createdAtFoo, _ = time.Parse(time.RFC3339, "2018-12-05T07:03:19+00:00") + updatedAt, _ = time.Parse(time.RFC3339, "2018-12-05T07:03:21+00:00") + + ConductorFoo = conductors.Conductor{ + CreatedAt: createdAtFoo, + UpdatedAt: updatedAt, + Hostname: "compute2.localdomain", + ConductorGroup: "", + Alive: true, + Drivers: []string{ + "ipmi", + }, + } +) + +// HandleConductorListSuccessfully sets up the test server to respond to a server List request. +func HandleConductorListSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/conductors", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + w.Header().Add("Content-Type", "application/json") + r.ParseForm() + + marker := r.Form.Get("marker") + switch marker { + case "": + fmt.Fprintf(w, ConductorListBody) + + case "9e5476bd-a4ec-4653-93d6-72c93aa682ba": + fmt.Fprintf(w, `{ "servers": [] }`) + default: + t.Fatalf("/conductors invoked with unexpected marker=[%s]", marker) + } + }) +} + +// HandleConductorListDetailSuccessfully sets up the test server to respond to a server List request. +func HandleConductorListDetailSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/conductors", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + w.Header().Add("Content-Type", "application/json") + r.ParseForm() + + fmt.Fprintf(w, ConductorListDetailBody) + }) +} + +func HandleConductorGetSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/conductors/1234asdf", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + th.TestHeader(t, r, "Accept", "application/json") + + fmt.Fprintf(w, SingleConductorBody) + }) +} diff --git a/openstack/baremetal/v1/conductors/testing/requests_test.go b/openstack/baremetal/v1/conductors/testing/requests_test.go new file mode 100644 index 0000000000..b05495a5fd --- /dev/null +++ b/openstack/baremetal/v1/conductors/testing/requests_test.go @@ -0,0 +1,106 @@ +package testing + +import ( + "testing" + + "github.com/gophercloud/gophercloud/openstack/baremetal/v1/conductors" + "github.com/gophercloud/gophercloud/pagination" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestListConductors(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleConductorListSuccessfully(t) + + pages := 0 + err := conductors.List(client.ServiceClient(), conductors.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + pages++ + + actual, err := conductors.ExtractConductors(page) + if err != nil { + return false, err + } + + if len(actual) != 2 { + t.Fatalf("Expected 2 conductors, got %d", len(actual)) + } + th.AssertEquals(t, "compute1.localdomain", actual[0].Hostname) + th.AssertEquals(t, "compute2.localdomain", actual[1].Hostname) + + return true, nil + }) + + th.AssertNoErr(t, err) + + if pages != 1 { + t.Errorf("Expected 1 page, saw %d", pages) + } +} + +func TestListDetailConductors(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleConductorListDetailSuccessfully(t) + + pages := 0 + err := conductors.List(client.ServiceClient(), conductors.ListOpts{Detail: true}).EachPage(func(page pagination.Page) (bool, error) { + pages++ + + actual, err := conductors.ExtractConductors(page) + if err != nil { + return false, err + } + + if len(actual) != 2 { + t.Fatalf("Expected 2 conductors, got %d", len(actual)) + } + th.AssertEquals(t, "compute1.localdomain", actual[0].Hostname) + th.AssertEquals(t, false, actual[0].Alive) + th.AssertEquals(t, "compute2.localdomain", actual[1].Hostname) + th.AssertEquals(t, true, actual[1].Alive) + + return true, nil + }) + + th.AssertNoErr(t, err) + + if pages != 1 { + t.Errorf("Expected 1 page, saw %d", pages) + } +} + +func TestListOpts(t *testing.T) { + // Detail cannot take Fields + optsDetail := conductors.ListOpts{ + Fields: []string{"hostname", "alive"}, + Detail: true, + } + + opts := conductors.ListOpts{ + Fields: []string{"hostname", "alive"}, + } + + _, err := optsDetail.ToConductorListQuery() + th.AssertEquals(t, err.Error(), "cannot have both fields and detail options for conductors") + + // Regular ListOpts can + query, err := opts.ToConductorListQuery() + th.AssertEquals(t, query, "?fields=hostname&fields=alive") + th.AssertNoErr(t, err) +} + +func TestGetConductor(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleConductorGetSuccessfully(t) + + c := client.ServiceClient() + actual, err := conductors.Get(c, "1234asdf").Extract() + if err != nil { + t.Fatalf("Unexpected Get error: %v", err) + } + + th.CheckDeepEquals(t, ConductorFoo, *actual) +} diff --git a/openstack/baremetal/v1/conductors/urls.go b/openstack/baremetal/v1/conductors/urls.go new file mode 100644 index 0000000000..a52e1e5ca5 --- /dev/null +++ b/openstack/baremetal/v1/conductors/urls.go @@ -0,0 +1,11 @@ +package conductors + +import "github.com/gophercloud/gophercloud" + +func listURL(client *gophercloud.ServiceClient) string { + return client.ServiceURL("conductors") +} + +func getURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL("conductors", id) +} From 0fe4b1bd9c2ed2e424b076dc9c02c834db62a361 Mon Sep 17 00:00:00 2001 From: Duc Truong <duct@nvidia.com> Date: Mon, 7 Aug 2023 14:01:40 -0700 Subject: [PATCH 281/360] Add conductor API to Baremetal V1 --- .../openstack/baremetal/v1/conductors_test.go | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/acceptance/openstack/baremetal/v1/conductors_test.go b/acceptance/openstack/baremetal/v1/conductors_test.go index f587d6fc3f..861fa31f6b 100644 --- a/acceptance/openstack/baremetal/v1/conductors_test.go +++ b/acceptance/openstack/baremetal/v1/conductors_test.go @@ -1,5 +1,5 @@ -//go:build acceptance || baremetal || ports -// +build acceptance baremetal ports +//go:build acceptance || baremetal || conductors +// +build acceptance baremetal conductors package v1 @@ -7,6 +7,7 @@ import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/baremetal/v1/conductors" "github.com/gophercloud/gophercloud/pagination" @@ -18,15 +19,17 @@ func TestConductorsList(t *testing.T) { client, err := clients.NewBareMetalV1Client() th.AssertNoErr(t, err) - client.Microversion = "1.53" + client.Microversion = "1.49" err = conductors.List(client, conductors.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { - _, err := conductors.ExtractConductors(page) + conductorList, err := conductors.ExtractConductors(page) if err != nil { return false, err } - return false, nil + tools.PrintResource(t, conductorList) + + return true, nil }) th.AssertNoErr(t, err) } From 9697b21450f275d1e5e676128a08687f6fea2d10 Mon Sep 17 00:00:00 2001 From: Duc Truong <duct@nvidia.com> Date: Tue, 8 Aug 2023 12:04:08 -0700 Subject: [PATCH 282/360] Add get conductor acceptance test --- acceptance/openstack/baremetal/v1/conductors_test.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/acceptance/openstack/baremetal/v1/conductors_test.go b/acceptance/openstack/baremetal/v1/conductors_test.go index 861fa31f6b..5324a7aa1c 100644 --- a/acceptance/openstack/baremetal/v1/conductors_test.go +++ b/acceptance/openstack/baremetal/v1/conductors_test.go @@ -14,7 +14,7 @@ import ( th "github.com/gophercloud/gophercloud/testhelper" ) -func TestConductorsList(t *testing.T) { +func TestConductorsListAndGet(t *testing.T) { clients.RequireLong(t) client, err := clients.NewBareMetalV1Client() @@ -29,6 +29,13 @@ func TestConductorsList(t *testing.T) { tools.PrintResource(t, conductorList) + if len(conductorList) > 0 { + conductor, err := conductors.Get(client, conductorList[0].Hostname).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, conductor) + } + return true, nil }) th.AssertNoErr(t, err) From 45e856e8784ef02757c332cb32231063b7afaf17 Mon Sep 17 00:00:00 2001 From: Emilien Macchi <emacchi@redhat.com> Date: Mon, 21 Aug 2023 13:16:21 -0400 Subject: [PATCH 283/360] networking/v2/ports: allow list filter by security group neutron v2 ports APIs allow to list ports by security group already: https://docs.openstack.org/api-ref/network/v2/#show-port-details This patch adds the `SecurityGroups` field to `ListOpts`. One way to filter the ports by security group can be done with the following code: ``` listOpts := ports.ListOpts{ SecurityGroups: []string{"2183457b-70cc-4fd0-a2dc-95323fa19e45"} } allPages, err := ports.List(networkClient, listOpts).AllPages() if err != nil { panic(err) } allPorts, err := ports.ExtractPorts(allPages) if err != nil { panic(err) } for _, port := range allPorts { fmt.Printf("%+v\n", port) } ``` --- openstack/networking/v2/ports/requests.go | 41 ++++++++++++----------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/openstack/networking/v2/ports/requests.go b/openstack/networking/v2/ports/requests.go index 48f9985643..805f0e5b99 100644 --- a/openstack/networking/v2/ports/requests.go +++ b/openstack/networking/v2/ports/requests.go @@ -21,26 +21,27 @@ type ListOptsBuilder interface { // by a particular port attribute. SortDir sets the direction, and is either // `asc' or `desc'. Marker and Limit are used for pagination. type ListOpts struct { - Status string `q:"status"` - Name string `q:"name"` - Description string `q:"description"` - AdminStateUp *bool `q:"admin_state_up"` - NetworkID string `q:"network_id"` - TenantID string `q:"tenant_id"` - ProjectID string `q:"project_id"` - DeviceOwner string `q:"device_owner"` - MACAddress string `q:"mac_address"` - ID string `q:"id"` - DeviceID string `q:"device_id"` - Limit int `q:"limit"` - Marker string `q:"marker"` - SortKey string `q:"sort_key"` - SortDir string `q:"sort_dir"` - Tags string `q:"tags"` - TagsAny string `q:"tags-any"` - NotTags string `q:"not-tags"` - NotTagsAny string `q:"not-tags-any"` - FixedIPs []FixedIPOpts + Status string `q:"status"` + Name string `q:"name"` + Description string `q:"description"` + AdminStateUp *bool `q:"admin_state_up"` + NetworkID string `q:"network_id"` + TenantID string `q:"tenant_id"` + ProjectID string `q:"project_id"` + DeviceOwner string `q:"device_owner"` + MACAddress string `q:"mac_address"` + ID string `q:"id"` + DeviceID string `q:"device_id"` + Limit int `q:"limit"` + Marker string `q:"marker"` + SortKey string `q:"sort_key"` + SortDir string `q:"sort_dir"` + Tags string `q:"tags"` + TagsAny string `q:"tags-any"` + NotTags string `q:"not-tags"` + NotTagsAny string `q:"not-tags-any"` + SecurityGroups []string `q:"security_groups"` + FixedIPs []FixedIPOpts } type FixedIPOpts struct { From 853953105f2d9179024f03880a065a6489df1a1b Mon Sep 17 00:00:00 2001 From: Pierre Prinetti <pierreprinetti@redhat.com> Date: Wed, 30 Aug 2023 15:06:55 +0200 Subject: [PATCH 284/360] Prepare release v1.6.0 --- CHANGELOG.md | 21 +++++++++++++++++++++ provider_client.go | 2 +- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e19d5af517..fb9d4172a8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,24 @@ +## 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: diff --git a/provider_client.go b/provider_client.go index 6cfb14fd7e..d1955b65ab 100644 --- a/provider_client.go +++ b/provider_client.go @@ -14,7 +14,7 @@ import ( // DefaultUserAgent is the default User-Agent string set in the request header. const ( - DefaultUserAgent = "gophercloud/v1.5.0" + DefaultUserAgent = "gophercloud/v1.6.0" DefaultMaxBackoffRetries = 60 ) From 224322e323b6029379113fc1c0bc97d13ccc7f10 Mon Sep 17 00:00:00 2001 From: Emilien Macchi <emacchi@redhat.com> Date: Tue, 29 Aug 2023 10:23:31 -0400 Subject: [PATCH 285/360] workflow: remove semver-require Now that semver label is always set but the bot, we don't need to ensure that a label was set. --- .github/workflows/semver-require.yaml | 17 ----------------- 1 file changed, 17 deletions(-) delete mode 100644 .github/workflows/semver-require.yaml diff --git a/.github/workflows/semver-require.yaml b/.github/workflows/semver-require.yaml deleted file mode 100644 index d584899306..0000000000 --- a/.github/workflows/semver-require.yaml +++ /dev/null @@ -1,17 +0,0 @@ -name: Verify PR Labels -on: - pull_request: - types: - - opened - - labeled - - unlabeled - - synchronize -jobs: - semver: - runs-on: ubuntu-latest - steps: - - uses: mheap/github-action-required-labels@v5 - with: - mode: exactly - count: 1 - labels: "semver:patch, semver:minor, semver:major" From f826aede41ae83a7127bcff96bbb272d04e0156f Mon Sep 17 00:00:00 2001 From: Emilien Macchi <emilien@redhat.com> Date: Fri, 18 Nov 2022 09:50:53 -0500 Subject: [PATCH 286/360] Add Github Workflow for checking Go API compatibility on PRs It'll automatically set the semver label for a given PR. Limitation: files under `/acceptance` or named `doc.go`, `fixtures.go` and `fixture.go` which don't count as semver, will count in this new job. This is because we would need to add a commit that exclude these files, then rebase the PR on top of that and run `go-apidiff`. This is too complex for now and might be addressed later. (cherry picked from commit 00da6f70afacb50378ce7cc0d6413359aa98899a) --- .github/workflows/semver-auto.yaml | 51 +++++++++++++++++++++++++++ .github/workflows/semver-unlabel.yaml | 21 ----------- 2 files changed, 51 insertions(+), 21 deletions(-) create mode 100644 .github/workflows/semver-auto.yaml delete mode 100644 .github/workflows/semver-unlabel.yaml diff --git a/.github/workflows/semver-auto.yaml b/.github/workflows/semver-auto.yaml new file mode 100644 index 0000000000..a5fbf1cee6 --- /dev/null +++ b/.github/workflows/semver-auto.yaml @@ -0,0 +1,51 @@ +name: Add PR semver labels +on: + pull_request_target: + types: [opened, synchronize, reopened] +jobs: + go-apidiff: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + ref: ${{ github.event.pull_request.head.sha }} + token: ${{ secrets.GITHUB_TOKEN }} + + - uses: actions/setup-go@v2 + with: + go-version: '1' + + - name: Remove the semver label + uses: actions-ecosystem/action-remove-labels@2ce5d41b4b6aa8503e285553f75ed56e0a40bae0 + with: + labels: | + semver:patch + semver:minor + semver:major + github_token: ${{ secrets.GITHUB_TOKEN }} + + - name: Checking Go API Compatibility + id: go-apidiff + uses: joelanford/go-apidiff@v0.7.0 + + - name: Add semver:patch label + if: always() && steps.go-apidiff.outputs.semver-type == 'patch' + uses: actions-ecosystem/action-add-labels@bd52874380e3909a1ac983768df6976535ece7f8 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + labels: semver:patch + + - name: Add semver:minor label + if: always() && steps.go-apidiff.outputs.semver-type == 'minor' + uses: actions-ecosystem/action-add-labels@bd52874380e3909a1ac983768df6976535ece7f8 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + labels: semver:minor + + - name: Add semver:major label + if: always() && steps.go-apidiff.outputs.semver-type == 'major' + uses: actions-ecosystem/action-add-labels@bd52874380e3909a1ac983768df6976535ece7f8 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + labels: semver:major diff --git a/.github/workflows/semver-unlabel.yaml b/.github/workflows/semver-unlabel.yaml deleted file mode 100644 index 9fdf5558e4..0000000000 --- a/.github/workflows/semver-unlabel.yaml +++ /dev/null @@ -1,21 +0,0 @@ -name: Reset PR labels on push - -# **What it does**: When the content of a PR changes, this workflow removes the semver label -# **Why we have it**: To make sure semver labels are up-to-date. -# **Who does it impact**: Pull requests. - -on: - pull_request_target: - types: - - synchronize - -jobs: - semver: - runs-on: ubuntu-latest - steps: - - name: Remove the semver label - uses: andymckay/labeler@1.0.4 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - remove-labels: "semver:patch, semver:minor, semver:major" From 8fe63e9602cd0e80c04fdfed81a6800a8211bcf1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 29 Aug 2023 09:06:27 +0000 Subject: [PATCH 287/360] Bump actions/setup-go from 2 to 4 Bumps [actions/setup-go](https://github.com/actions/setup-go) from 2 to 4. - [Release notes](https://github.com/actions/setup-go/releases) - [Commits](https://github.com/actions/setup-go/compare/v2...v4) --- updated-dependencies: - dependency-name: actions/setup-go dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> (cherry picked from commit 86e726d1a1072a2ebd10ce692578efac3bdb2643) --- .github/workflows/semver-auto.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/semver-auto.yaml b/.github/workflows/semver-auto.yaml index a5fbf1cee6..ecb3be0f05 100644 --- a/.github/workflows/semver-auto.yaml +++ b/.github/workflows/semver-auto.yaml @@ -12,7 +12,7 @@ jobs: ref: ${{ github.event.pull_request.head.sha }} token: ${{ secrets.GITHUB_TOKEN }} - - uses: actions/setup-go@v2 + - uses: actions/setup-go@v4 with: go-version: '1' From 8bc2f1abdb504d809bb83f1620d53e140618a862 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 29 Aug 2023 09:06:29 +0000 Subject: [PATCH 288/360] Bump actions-ecosystem/action-add-labels from 1.1.0 to 1.1.3 Bumps [actions-ecosystem/action-add-labels](https://github.com/actions-ecosystem/action-add-labels) from 1.1.0 to 1.1.3. - [Release notes](https://github.com/actions-ecosystem/action-add-labels/releases) - [Commits](https://github.com/actions-ecosystem/action-add-labels/compare/bd52874380e3909a1ac983768df6976535ece7f8...18f1af5e3544586314bbe15c0273249c770b2daf) --- updated-dependencies: - dependency-name: actions-ecosystem/action-add-labels dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> (cherry picked from commit 8a032b26f39817e01dd42d1e3b3de49685872bd2) --- .github/workflows/semver-auto.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/semver-auto.yaml b/.github/workflows/semver-auto.yaml index ecb3be0f05..bc5322b7e4 100644 --- a/.github/workflows/semver-auto.yaml +++ b/.github/workflows/semver-auto.yaml @@ -31,21 +31,21 @@ jobs: - name: Add semver:patch label if: always() && steps.go-apidiff.outputs.semver-type == 'patch' - uses: actions-ecosystem/action-add-labels@bd52874380e3909a1ac983768df6976535ece7f8 + uses: actions-ecosystem/action-add-labels@18f1af5e3544586314bbe15c0273249c770b2daf with: github_token: ${{ secrets.GITHUB_TOKEN }} labels: semver:patch - name: Add semver:minor label if: always() && steps.go-apidiff.outputs.semver-type == 'minor' - uses: actions-ecosystem/action-add-labels@bd52874380e3909a1ac983768df6976535ece7f8 + uses: actions-ecosystem/action-add-labels@18f1af5e3544586314bbe15c0273249c770b2daf with: github_token: ${{ secrets.GITHUB_TOKEN }} labels: semver:minor - name: Add semver:major label if: always() && steps.go-apidiff.outputs.semver-type == 'major' - uses: actions-ecosystem/action-add-labels@bd52874380e3909a1ac983768df6976535ece7f8 + uses: actions-ecosystem/action-add-labels@18f1af5e3544586314bbe15c0273249c770b2daf with: github_token: ${{ secrets.GITHUB_TOKEN }} labels: semver:major From d8ced84de067c1b1d0d6e354d20c2667c9797c77 Mon Sep 17 00:00:00 2001 From: Emilien Macchi <emacchi@redhat.com> Date: Wed, 30 Aug 2023 09:13:38 -0400 Subject: [PATCH 289/360] workflow/semver-auto: rebase PR When testing if a PR is API backward compatible or not, we need to test against the base branch, not the head against the PR branched is based on. Otherwise it can lead to the case where a breaking change was merged after that a PR was proposed, go-apidiff will fail because it'll test the repo against base_ref. The PR needs to be rebased on base_ref so the actual compatibility can be verified. If the PR can't be rebase for any reason, we'll return a failure and add the label `semver:unknown`. (cherry picked from commit 3e0071fcd67b3fb055c7634e1191a704dae8a56e) --- .github/workflows/semver-auto.yaml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.github/workflows/semver-auto.yaml b/.github/workflows/semver-auto.yaml index bc5322b7e4..2f8436f0e8 100644 --- a/.github/workflows/semver-auto.yaml +++ b/.github/workflows/semver-auto.yaml @@ -12,6 +12,17 @@ jobs: ref: ${{ github.event.pull_request.head.sha }} token: ${{ secrets.GITHUB_TOKEN }} + - uses: peter-evans/rebase@56c359b35ff7ba8426d0fdb842958b35b1db827 + with: + base: ${{ github.base_ref }} + + - name: Add semver:unknown label + if: failure() + uses: actions-ecosystem/action-add-labels@18f1af5e3544586314bbe15c0273249c770b2daf + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + labels: semver:unknown + - uses: actions/setup-go@v4 with: go-version: '1' @@ -23,6 +34,7 @@ jobs: semver:patch semver:minor semver:major + semver:unknown github_token: ${{ secrets.GITHUB_TOKEN }} - name: Checking Go API Compatibility From c3db85a60e9acdb28aeab5bcd8d79bad62af8a18 Mon Sep 17 00:00:00 2001 From: Emilien Macchi <emacchi@redhat.com> Date: Fri, 1 Sep 2023 10:31:11 -0400 Subject: [PATCH 290/360] CI: Fix a typo in semver-auto A caracter is missing for the rebase action in git ref. (cherry picked from commit 9e9f560bd9c8534412f1ac6f0585f172a7f6a3f9) --- .github/workflows/semver-auto.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/semver-auto.yaml b/.github/workflows/semver-auto.yaml index 2f8436f0e8..e1c770189c 100644 --- a/.github/workflows/semver-auto.yaml +++ b/.github/workflows/semver-auto.yaml @@ -12,7 +12,7 @@ jobs: ref: ${{ github.event.pull_request.head.sha }} token: ${{ secrets.GITHUB_TOKEN }} - - uses: peter-evans/rebase@56c359b35ff7ba8426d0fdb842958b35b1db827 + - uses: peter-evans/rebase@56c359b35ff7ba8426d0fdb842958b35b1db8277 with: base: ${{ github.base_ref }} From 0df624b7018020a1af2c87b54dfc5e338eb5efbe Mon Sep 17 00:00:00 2001 From: Emilien Macchi <emacchi@redhat.com> Date: Fri, 1 Sep 2023 11:08:36 -0400 Subject: [PATCH 291/360] semver-auto: remove labels first Change the tasks order, so we clean semver labels first. This avoids the case when the rebase failed, and we still have the old label, and also the `semver:unknown`, which is confusing. (cherry picked from commit 8534afd5fbdd37c6822f582fc89737caeaa1aad9) --- .github/workflows/semver-auto.yaml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/semver-auto.yaml b/.github/workflows/semver-auto.yaml index e1c770189c..4ca90227c0 100644 --- a/.github/workflows/semver-auto.yaml +++ b/.github/workflows/semver-auto.yaml @@ -6,6 +6,16 @@ jobs: go-apidiff: runs-on: ubuntu-latest steps: + - name: Remove the semver labels + uses: actions-ecosystem/action-remove-labels@2ce5d41b4b6aa8503e285553f75ed56e0a40bae0 + with: + labels: | + semver:patch + semver:minor + semver:major + semver:unknown + github_token: ${{ secrets.GITHUB_TOKEN }} + - uses: actions/checkout@v3 with: fetch-depth: 0 @@ -27,16 +37,6 @@ jobs: with: go-version: '1' - - name: Remove the semver label - uses: actions-ecosystem/action-remove-labels@2ce5d41b4b6aa8503e285553f75ed56e0a40bae0 - with: - labels: | - semver:patch - semver:minor - semver:major - semver:unknown - github_token: ${{ secrets.GITHUB_TOKEN }} - - name: Checking Go API Compatibility id: go-apidiff uses: joelanford/go-apidiff@v0.7.0 From 9841cac733d4566e2792b704058ffd6548cc003f Mon Sep 17 00:00:00 2001 From: Emilien Macchi <emacchi@redhat.com> Date: Fri, 1 Sep 2023 11:18:31 -0400 Subject: [PATCH 292/360] semver-auto: manual rebase instead of external action I haven't found an action that just runs a rebase without pushing it into the repo. Let's just run `git rebase -i` for now. (cherry picked from commit 510ded837d5ce25911d57dc5a68cdf302e127c6a) --- .github/workflows/semver-auto.yaml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/semver-auto.yaml b/.github/workflows/semver-auto.yaml index 4ca90227c0..0cd6080bc5 100644 --- a/.github/workflows/semver-auto.yaml +++ b/.github/workflows/semver-auto.yaml @@ -22,9 +22,11 @@ jobs: ref: ${{ github.event.pull_request.head.sha }} token: ${{ secrets.GITHUB_TOKEN }} - - uses: peter-evans/rebase@56c359b35ff7ba8426d0fdb842958b35b1db8277 - with: - base: ${{ github.base_ref }} + - name: Rebase the PR against origin/github.base_ref to ensure actual API compatibility + run: | + git rebase -i origin/${{ github.base_ref }} + env: + GIT_SEQUENCE_EDITOR: '/usr/bin/true' - name: Add semver:unknown label if: failure() From a0dde2411d273a7be321839a932d9b528375f57c Mon Sep 17 00:00:00 2001 From: Emilien Macchi <emacchi@redhat.com> Date: Tue, 5 Sep 2023 10:53:02 -0400 Subject: [PATCH 293/360] semver-auto: tell who we are during rebase To avoid the `*** Please tell me who you are` error when a PR is locally rebased to find out which semver label to apply, let's just set fake user.email and user.name, which aren't used anyway since the PR isn't actually rebased, but just for the workflow so we can run `go-apidiff` which itself requires code to be committed. (cherry picked from commit 13358932e257c194233e0e4865b3860b985203f9) --- .github/workflows/semver-auto.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/semver-auto.yaml b/.github/workflows/semver-auto.yaml index 0cd6080bc5..2aa66b83fd 100644 --- a/.github/workflows/semver-auto.yaml +++ b/.github/workflows/semver-auto.yaml @@ -24,6 +24,8 @@ jobs: - name: Rebase the PR against origin/github.base_ref to ensure actual API compatibility run: | + git config --global user.email "localrebase@gophercloud.io" + git config --global user.name "Local rebase" git rebase -i origin/${{ github.base_ref }} env: GIT_SEQUENCE_EDITOR: '/usr/bin/true' From 0297174d63057fa26c13077b648c077f38402b44 Mon Sep 17 00:00:00 2001 From: Pierre Prinetti <pierreprinetti@redhat.com> Date: Fri, 8 Sep 2023 12:26:13 +0200 Subject: [PATCH 294/360] Fix typos in comments --- openstack/imageservice/v2/images/results.go | 2 +- openstack/keymanager/v1/secrets/results.go | 22 +++++++++---------- .../v2/extensions/qos/ruletypes/doc.go | 2 +- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/openstack/imageservice/v2/images/results.go b/openstack/imageservice/v2/images/results.go index 1b27a15495..96fd91a2ca 100644 --- a/openstack/imageservice/v2/images/results.go +++ b/openstack/imageservice/v2/images/results.go @@ -76,7 +76,7 @@ type Image struct { CreatedAt time.Time `json:"created_at"` // UpdatedAt is the date when the last change has been made to the image or - // it's properties. + // its properties. UpdatedAt time.Time `json:"updated_at"` // File is the trailing path after the glance endpoint that represent the diff --git a/openstack/keymanager/v1/secrets/results.go b/openstack/keymanager/v1/secrets/results.go index f76b977520..46e6860098 100644 --- a/openstack/keymanager/v1/secrets/results.go +++ b/openstack/keymanager/v1/secrets/results.go @@ -93,14 +93,14 @@ type CreateResult struct { commonResult } -// UpdateResult is the response from an Update operation. Call its ExtractErr to -// determine if the request succeeded or failed. +// UpdateResult is the response from an Update operation. Call its ExtractErr +// method to determine if the request succeeded or failed. type UpdateResult struct { gophercloud.ErrResult } -// DeleteResult is the response from a Delete operation. Call its ExtractErr to -// determine if the request succeeded or failed. +// DeleteResult is the response from a Delete operation. Call its ExtractErr +// method to determine if the request succeeded or failed. type DeleteResult struct { gophercloud.ErrResult } @@ -112,11 +112,11 @@ type PayloadResult struct { Body io.ReadCloser } -// Extract is a function that takes a PayloadResult's io.Reader body -// and reads all available data into a slice of bytes. Please be aware that due -// to the nature of io.Reader is forward-only - meaning that it can only be read -// once and not rewound. You can recreate a reader from the output of this -// function by using bytes.NewReader(downloadBytes) +// Extract is a method that takes a PayloadResult's io.Reader body and reads +// all available data into a slice of bytes. Please be aware that its io.Reader +// is forward-only - meaning that it can only be read once and not rewound. You +// can recreate a reader from the output of this function by using +// bytes.NewReader(downloadBytes) func (r PayloadResult) Extract() ([]byte, error) { if r.Err != nil { return nil, r.Err @@ -215,7 +215,7 @@ func (r MetadatumResult) Extract() (*Metadatum, error) { } // MetadatumCreateResult is the response from a metadata Create operation. Call -// it's ExtractErr to determine if the request succeeded or failed. +// its ExtractErr method to determine if the request succeeded or failed. // // NOTE: This could be a MetadatumResponse but, at the time of testing, it looks // like Barbican was returning errneous JSON in the response. @@ -224,7 +224,7 @@ type MetadatumCreateResult struct { } // MetadatumDeleteResult is the response from a metadatum Delete operation. Call -// its ExtractErr to determine if the request succeeded or failed. +// its ExtractErr method to determine if the request succeeded or failed. type MetadatumDeleteResult struct { gophercloud.ErrResult } diff --git a/openstack/networking/v2/extensions/qos/ruletypes/doc.go b/openstack/networking/v2/extensions/qos/ruletypes/doc.go index c36081b485..e87efd96bd 100644 --- a/openstack/networking/v2/extensions/qos/ruletypes/doc.go +++ b/openstack/networking/v2/extensions/qos/ruletypes/doc.go @@ -15,7 +15,7 @@ Example of Listing QoS rule types fmt.Printf("%v <- Rule Types\n", rules) -Example of Getting a single QoS rule type by it's name +Example of Getting a single QoS rule type by name ruleTypeName := "bandwidth_limit" From b47ab2f753052aa4a1dbf58260a6f35fdf6444b4 Mon Sep 17 00:00:00 2001 From: Matt Pryor <matt@stackhpc.com> Date: Thu, 14 Sep 2023 13:59:06 +0100 Subject: [PATCH 295/360] Add tag field to compute block_device_v2 (cherry picked from commit 0e644e48ef6d1c4ce759755884073147e15a1957) --- openstack/compute/v2/extensions/bootfromvolume/requests.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/openstack/compute/v2/extensions/bootfromvolume/requests.go b/openstack/compute/v2/extensions/bootfromvolume/requests.go index 096d8be7ef..17f11b3849 100644 --- a/openstack/compute/v2/extensions/bootfromvolume/requests.go +++ b/openstack/compute/v2/extensions/bootfromvolume/requests.go @@ -79,6 +79,12 @@ type BlockDevice struct { // 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"` } // CreateOptsExt is a structure that extends the server `CreateOpts` structure From 18940e4f5669af377b35258f871b06e437849254 Mon Sep 17 00:00:00 2001 From: Matt Pryor <matt@stackhpc.com> Date: Thu, 14 Sep 2023 15:19:35 +0100 Subject: [PATCH 296/360] Add requests test for new field (cherry picked from commit f218ca0823da721823870bb0c8cdb26302924326) --- .../bootfromvolume/testing/fixtures.go | 50 +++++++++++++++++++ .../bootfromvolume/testing/requests_test.go | 6 +++ 2 files changed, 56 insertions(+) diff --git a/openstack/compute/v2/extensions/bootfromvolume/testing/fixtures.go b/openstack/compute/v2/extensions/bootfromvolume/testing/fixtures.go index cb89173aaa..39b09c7fe7 100644 --- a/openstack/compute/v2/extensions/bootfromvolume/testing/fixtures.go +++ b/openstack/compute/v2/extensions/bootfromvolume/testing/fixtures.go @@ -308,3 +308,53 @@ var NewVolumeTypeRequest = bootfromvolume.CreateOptsExt{ }, }, } + +const ExpectedImageAndExistingVolumeWithTagRequest = ` +{ + "server": { + "name": "createdserver", + "imageRef": "asdfasdfasdf", + "flavorRef": "performance1-1", + "block_device_mapping_v2":[ + { + "boot_index": 0, + "delete_on_termination": true, + "destination_type":"local", + "source_type":"image", + "uuid":"asdfasdfasdf" + }, + { + "boot_index": -1, + "delete_on_termination": true, + "destination_type":"volume", + "source_type":"volume", + "tag": "volume-tag", + "uuid":"123456", + "volume_size": 1 + } + ] + } +} +` + +var ImageAndExistingVolumeWithTagRequest = bootfromvolume.CreateOptsExt{ + CreateOptsBuilder: BaseCreateOptsWithImageRef, + BlockDevice: []bootfromvolume.BlockDevice{ + { + BootIndex: 0, + DeleteOnTermination: true, + DestinationType: bootfromvolume.DestinationLocal, + SourceType: bootfromvolume.SourceImage, + UUID: "asdfasdfasdf", + }, + { + BootIndex: -1, + DeleteOnTermination: true, + DestinationType: bootfromvolume.DestinationVolume, + SourceType: bootfromvolume.SourceVolume, + Tag: "volume-tag", + UUID: "123456", + VolumeSize: 1, + }, + }, +} diff --git a/openstack/compute/v2/extensions/bootfromvolume/testing/requests_test.go b/openstack/compute/v2/extensions/bootfromvolume/testing/requests_test.go index 69afc865c9..22d11cc823 100644 --- a/openstack/compute/v2/extensions/bootfromvolume/testing/requests_test.go +++ b/openstack/compute/v2/extensions/bootfromvolume/testing/requests_test.go @@ -47,3 +47,9 @@ func TestBootFromNewVolumeType(t *testing.T) { th.AssertNoErr(t, err) th.CheckJSONEquals(t, ExpectedNewVolumeTypeRequest, actual) } + +func TestAttachExistingVolumeWithTag(t *testing.T) { + actual, err := ImageAndExistingVolumeWithTagRequest.ToServerCreateMap() + th.AssertNoErr(t, err) + th.CheckJSONEquals(t, ExpectedImageAndExistingVolumeWithTagRequest, actual) +} From 9fc151e76705ed899895f4297f8ddf25dd30b237 Mon Sep 17 00:00:00 2001 From: Emilien Macchi <emacchi@redhat.com> Date: Wed, 20 Sep 2023 13:37:15 -0400 Subject: [PATCH 297/360] acceptance/compute: remove flavor ID check If the client uses a microversion > 2.46, the flavor ID doesn't exist. See https://docs.openstack.org/api-ref/compute/#id30 To avoid complexity when checking the flavor, let's just remove that test. (cherry picked from commit 93e5d0fec756becea90038b5fd3b9354528690e5) --- acceptance/openstack/compute/v2/compute.go | 1 - 1 file changed, 1 deletion(-) diff --git a/acceptance/openstack/compute/v2/compute.go b/acceptance/openstack/compute/v2/compute.go index c34a5f5774..7a5c696e68 100644 --- a/acceptance/openstack/compute/v2/compute.go +++ b/acceptance/openstack/compute/v2/compute.go @@ -178,7 +178,6 @@ func CreateBootableVolumeServer(t *testing.T, client *gophercloud.ServiceClient, } th.AssertEquals(t, newServer.Name, name) - th.AssertEquals(t, newServer.Flavor["id"], choices.FlavorID) return newServer, nil } From 4ecebfb2c350809da019765c3dc0caf4a5ccd9ec Mon Sep 17 00:00:00 2001 From: Emilien Macchi <emacchi@redhat.com> Date: Wed, 20 Sep 2023 11:06:50 -0400 Subject: [PATCH 298/360] Add acceptance tests for tag field to compute block_device_v2 (cherry picked from commit 4fd4c99bff16eca3cdbeae1ec01af260e77aac3f) --- acceptance/openstack/compute/v2/bootfromvolume_test.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/acceptance/openstack/compute/v2/bootfromvolume_test.go b/acceptance/openstack/compute/v2/bootfromvolume_test.go index 9980660463..3296c84627 100644 --- a/acceptance/openstack/compute/v2/bootfromvolume_test.go +++ b/acceptance/openstack/compute/v2/bootfromvolume_test.go @@ -51,6 +51,11 @@ func TestBootFromNewVolume(t *testing.T) { choices, err := clients.AcceptanceTestChoicesFromEnv() th.AssertNoErr(t, err) + // minimum required microversion for getting volume tags is 2.70 + // https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id64 + client.Microversion = "2.70" + + tagName := "tag1" blockDevices := []bootfromvolume.BlockDevice{ { DeleteOnTermination: true, @@ -58,6 +63,7 @@ func TestBootFromNewVolume(t *testing.T) { SourceType: bootfromvolume.SourceImage, UUID: choices.ImageID, VolumeSize: 2, + Tag: tagName, }, } @@ -73,6 +79,8 @@ func TestBootFromNewVolume(t *testing.T) { tools.PrintResource(t, server) tools.PrintResource(t, attachments) + attachmentTag := *attachments[0].Tag + th.AssertEquals(t, attachmentTag, tagName) if server.Image != nil { t.Fatalf("server image should be nil") From 7eb68c102c4b392c0e58cdf48cf8699a3a80c265 Mon Sep 17 00:00:00 2001 From: Emilien Macchi <emacchi@redhat.com> Date: Thu, 7 Sep 2023 15:23:01 -0400 Subject: [PATCH 299/360] acceptance/volumeattachment: don't specify Connector With `lioadm` being the recommended iscsi helper, we need a real IQN; not a fake one. Let's just not specify the connector. Related #2768 (cherry picked from commit 440873f734970a2a19669f19af999977df1e91ae) --- .../blockstorage/v3/volumeattachments.go | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/acceptance/openstack/blockstorage/v3/volumeattachments.go b/acceptance/openstack/blockstorage/v3/volumeattachments.go index 510b6841ca..87eb0d92bd 100644 --- a/acceptance/openstack/blockstorage/v3/volumeattachments.go +++ b/acceptance/openstack/blockstorage/v3/volumeattachments.go @@ -20,10 +20,6 @@ func CreateVolumeAttachment(t *testing.T, client *gophercloud.ServiceClient, vol attachOpts := &attachments.CreateOpts{ VolumeUUID: volume.ID, InstanceUUID: server.ID, - Connector: map[string]interface{}{ - "mode": "rw", - "initiator": "fake", - }, } t.Logf("Attempting to attach volume %s to server %s", volume.ID, server.ID) @@ -56,20 +52,6 @@ func CreateVolumeAttachment(t *testing.T, client *gophercloud.ServiceClient, vol return err } - /* - // Not clear how perform a proper update, OpenStack returns "Unable to update the attachment." - updateOpts := &attachments.UpdateOpts{ - Connector: map[string]interface{}{ - "mode": "ro", - "initiator": "fake", - }, - } - attachment, err = attachments.Update(client, attachment.ID, updateOpts).Extract() - if err != nil { - return err - } - */ - listOpts := &attachments.ListOpts{ VolumeID: volume.ID, InstanceID: server.ID, From dafbcd8d30c77e31f43570821e5ff2abca75b365 Mon Sep 17 00:00:00 2001 From: Emilien Macchi <emacchi@redhat.com> Date: Thu, 7 Sep 2023 09:17:33 -0400 Subject: [PATCH 300/360] ci/functional: switch LIO target instead of tgt tgt has been broken for some time in the upstream CI and we were told by Cinder devs to use lioadm for the helper. Let's switch to it for more stable jobs. (cherry picked from commit f4ed08af2924655f97c7f4eeca5269ea5fa1a0fc) --- .github/workflows/functional-blockstorage.yaml | 2 +- .github/workflows/functional-compute.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/functional-blockstorage.yaml b/.github/workflows/functional-blockstorage.yaml index 4dfd04de59..e3cd9adf9b 100644 --- a/.github/workflows/functional-blockstorage.yaml +++ b/.github/workflows/functional-blockstorage.yaml @@ -37,7 +37,7 @@ jobs: with: branch: ${{ matrix.openstack_version }} conf_overrides: | - CINDER_ISCSI_HELPER=tgtadm + CINDER_ISCSI_HELPER=lioadm enabled_services: 's-account,s-container,s-object,s-proxy,c-bak' - name: Checkout go uses: actions/setup-go@v4 diff --git a/.github/workflows/functional-compute.yaml b/.github/workflows/functional-compute.yaml index 99448f7e48..d12512ec0d 100644 --- a/.github/workflows/functional-compute.yaml +++ b/.github/workflows/functional-compute.yaml @@ -37,7 +37,7 @@ jobs: with: branch: ${{ matrix.openstack_version }} conf_overrides: | - CINDER_ISCSI_HELPER=tgtadm + CINDER_ISCSI_HELPER=lioadm - name: Checkout go uses: actions/setup-go@v4 with: From a96ae3df2f10151162246b5f8873735bce5d7f8b Mon Sep 17 00:00:00 2001 From: Emilien Macchi <emacchi@redhat.com> Date: Tue, 5 Sep 2023 22:06:24 -0400 Subject: [PATCH 301/360] ci/functional: add `antelope` release in the matrix (cherry picked from commit 4abe682d402735186fed3f084d7289655f59824b) --- .github/workflows/functional-baremetal.yaml | 3 +++ .github/workflows/functional-basic.yaml | 3 +++ .github/workflows/functional-blockstorage.yaml | 3 +++ .github/workflows/functional-clustering.yaml | 3 +++ .github/workflows/functional-compute.yaml | 3 +++ .github/workflows/functional-containerinfra.yaml | 3 +++ .github/workflows/functional-dns.yaml | 3 +++ .github/workflows/functional-identity.yaml | 3 +++ .github/workflows/functional-imageservice.yaml | 3 +++ .github/workflows/functional-keymanager.yaml | 3 +++ .github/workflows/functional-loadbalancer.yaml | 3 +++ .github/workflows/functional-messaging.yaml | 3 +++ .github/workflows/functional-networking.yaml | 3 +++ .github/workflows/functional-objectstorage.yaml | 3 +++ .github/workflows/functional-orchestration.yaml | 3 +++ .github/workflows/functional-placement.yaml | 3 +++ .github/workflows/functional-sharedfilesystems.yaml | 3 +++ 17 files changed, 51 insertions(+) diff --git a/.github/workflows/functional-baremetal.yaml b/.github/workflows/functional-baremetal.yaml index 277245d1c6..e3a55ad4f6 100644 --- a/.github/workflows/functional-baremetal.yaml +++ b/.github/workflows/functional-baremetal.yaml @@ -12,6 +12,9 @@ jobs: openstack_version: ["master"] ubuntu_version: ["20.04"] include: + - name: "antelope" + openstack_version: "stable/2023.1" + ubuntu_version: "22.04" - name: "zed" openstack_version: "stable/zed" ubuntu_version: "20.04" diff --git a/.github/workflows/functional-basic.yaml b/.github/workflows/functional-basic.yaml index 982e006151..93f412f346 100644 --- a/.github/workflows/functional-basic.yaml +++ b/.github/workflows/functional-basic.yaml @@ -15,6 +15,9 @@ jobs: openstack_version: ["master"] ubuntu_version: ["20.04"] include: + - name: "antelope" + openstack_version: "stable/2023.1" + ubuntu_version: "22.04" - name: "zed" openstack_version: "stable/zed" ubuntu_version: "20.04" diff --git a/.github/workflows/functional-blockstorage.yaml b/.github/workflows/functional-blockstorage.yaml index e3cd9adf9b..ed6fb9187f 100644 --- a/.github/workflows/functional-blockstorage.yaml +++ b/.github/workflows/functional-blockstorage.yaml @@ -12,6 +12,9 @@ jobs: openstack_version: ["master"] ubuntu_version: ["20.04"] include: + - name: "antelope" + openstack_version: "stable/2023.1" + ubuntu_version: "22.04" - name: "zed" openstack_version: "stable/zed" ubuntu_version: "20.04" diff --git a/.github/workflows/functional-clustering.yaml b/.github/workflows/functional-clustering.yaml index fa9a8a5a46..3a08f8b1d6 100644 --- a/.github/workflows/functional-clustering.yaml +++ b/.github/workflows/functional-clustering.yaml @@ -12,6 +12,9 @@ jobs: openstack_version: ["master"] ubuntu_version: ["22.04"] include: + - name: "antelope" + openstack_version: "stable/2023.1" + ubuntu_version: "22.04" - name: "zed" openstack_version: "stable/zed" ubuntu_version: "20.04" diff --git a/.github/workflows/functional-compute.yaml b/.github/workflows/functional-compute.yaml index d12512ec0d..15ab332bd2 100644 --- a/.github/workflows/functional-compute.yaml +++ b/.github/workflows/functional-compute.yaml @@ -12,6 +12,9 @@ jobs: openstack_version: ["master"] ubuntu_version: ["20.04"] include: + - name: "antelope" + openstack_version: "stable/2023.1" + ubuntu_version: "22.04" - name: "zed" openstack_version: "stable/zed" ubuntu_version: "20.04" diff --git a/.github/workflows/functional-containerinfra.yaml b/.github/workflows/functional-containerinfra.yaml index 9dbd8dc5da..6332af01ec 100644 --- a/.github/workflows/functional-containerinfra.yaml +++ b/.github/workflows/functional-containerinfra.yaml @@ -14,6 +14,9 @@ jobs: ubuntu_version: "20.04" devstack_conf_overrides: | enable_plugin magnum https://github.com/openstack/magnum master + - name: "antelope" + openstack_version: "stable/2023.1" + ubuntu_version: "22.04" - name: "zed" openstack_version: "stable/zed" ubuntu_version: "20.04" diff --git a/.github/workflows/functional-dns.yaml b/.github/workflows/functional-dns.yaml index a14e0fbf02..dcca8b4c54 100644 --- a/.github/workflows/functional-dns.yaml +++ b/.github/workflows/functional-dns.yaml @@ -15,6 +15,9 @@ jobs: ubuntu_version: "20.04" devstack_conf_overrides: | enable_plugin designate https://github.com/openstack/designate master + - name: "antelope" + openstack_version: "stable/2023.1" + ubuntu_version: "22.04" - name: "zed" openstack_version: "stable/zed" ubuntu_version: "20.04" diff --git a/.github/workflows/functional-identity.yaml b/.github/workflows/functional-identity.yaml index df0764beb0..ee510977ae 100644 --- a/.github/workflows/functional-identity.yaml +++ b/.github/workflows/functional-identity.yaml @@ -12,6 +12,9 @@ jobs: openstack_version: ["master"] ubuntu_version: ["20.04"] include: + - name: "antelope" + openstack_version: "stable/2023.1" + ubuntu_version: "22.04" - name: "zed" openstack_version: "stable/zed" ubuntu_version: "20.04" diff --git a/.github/workflows/functional-imageservice.yaml b/.github/workflows/functional-imageservice.yaml index 77ee23bad4..18bb18ae63 100644 --- a/.github/workflows/functional-imageservice.yaml +++ b/.github/workflows/functional-imageservice.yaml @@ -12,6 +12,9 @@ jobs: openstack_version: ["master"] ubuntu_version: ["20.04"] include: + - name: "antelope" + openstack_version: "stable/2023.1" + ubuntu_version: "22.04" - name: "zed" openstack_version: "stable/zed" ubuntu_version: "20.04" diff --git a/.github/workflows/functional-keymanager.yaml b/.github/workflows/functional-keymanager.yaml index 42f097c655..9865935091 100644 --- a/.github/workflows/functional-keymanager.yaml +++ b/.github/workflows/functional-keymanager.yaml @@ -12,6 +12,9 @@ jobs: openstack_version: ["master"] ubuntu_version: ["20.04"] include: + - name: "antelope" + openstack_version: "stable/2023.1" + ubuntu_version: "22.04" - name: "zed" openstack_version: "stable/zed" ubuntu_version: "20.04" diff --git a/.github/workflows/functional-loadbalancer.yaml b/.github/workflows/functional-loadbalancer.yaml index 0b8cc40f6b..142f8f10a4 100644 --- a/.github/workflows/functional-loadbalancer.yaml +++ b/.github/workflows/functional-loadbalancer.yaml @@ -12,6 +12,9 @@ jobs: openstack_version: ["master"] ubuntu_version: ["20.04"] include: + - name: "antelope" + openstack_version: "stable/2023.1" + ubuntu_version: "22.04" - name: "zed" openstack_version: "stable/zed" ubuntu_version: "20.04" diff --git a/.github/workflows/functional-messaging.yaml b/.github/workflows/functional-messaging.yaml index 8314a1b496..0fcf3c0c35 100644 --- a/.github/workflows/functional-messaging.yaml +++ b/.github/workflows/functional-messaging.yaml @@ -12,6 +12,9 @@ jobs: openstack_version: ["master"] ubuntu_version: ["22.04"] include: + - name: "antelope" + openstack_version: "stable/2023.1" + ubuntu_version: "22.04" - name: "zed" openstack_version: "stable/zed" ubuntu_version: "20.04" diff --git a/.github/workflows/functional-networking.yaml b/.github/workflows/functional-networking.yaml index feca87453b..f8cb132a16 100644 --- a/.github/workflows/functional-networking.yaml +++ b/.github/workflows/functional-networking.yaml @@ -15,6 +15,9 @@ jobs: devstack_conf_overrides: | enable_plugin neutron-dynamic-routing https://github.com/openstack/neutron-dynamic-routing master enable_plugin neutron-vpnaas https://github.com/openstack/neutron-vpnaas master + - name: "antelope" + openstack_version: "stable/2023.1" + ubuntu_version: "22.04" - name: "zed" openstack_version: "stable/zed" ubuntu_version: "20.04" diff --git a/.github/workflows/functional-objectstorage.yaml b/.github/workflows/functional-objectstorage.yaml index 125a7add35..1625993a34 100644 --- a/.github/workflows/functional-objectstorage.yaml +++ b/.github/workflows/functional-objectstorage.yaml @@ -12,6 +12,9 @@ jobs: openstack_version: ["master"] ubuntu_version: ["20.04"] include: + - name: "antelope" + openstack_version: "stable/2023.1" + ubuntu_version: "22.04" - name: "zed" openstack_version: "stable/zed" ubuntu_version: "20.04" diff --git a/.github/workflows/functional-orchestration.yaml b/.github/workflows/functional-orchestration.yaml index 90391c4520..730c40f79b 100644 --- a/.github/workflows/functional-orchestration.yaml +++ b/.github/workflows/functional-orchestration.yaml @@ -12,6 +12,9 @@ jobs: openstack_version: ["master"] ubuntu_version: ["20.04"] include: + - name: "antelope" + openstack_version: "stable/2023.1" + ubuntu_version: "22.04" - name: "zed" openstack_version: "stable/zed" ubuntu_version: "20.04" diff --git a/.github/workflows/functional-placement.yaml b/.github/workflows/functional-placement.yaml index 71b24efcce..02422329e7 100644 --- a/.github/workflows/functional-placement.yaml +++ b/.github/workflows/functional-placement.yaml @@ -12,6 +12,9 @@ jobs: openstack_version: ["master"] ubuntu_version: ["20.04"] include: + - name: "antelope" + openstack_version: "stable/2023.1" + ubuntu_version: "22.04" - name: "zed" openstack_version: "stable/zed" ubuntu_version: "20.04" diff --git a/.github/workflows/functional-sharedfilesystems.yaml b/.github/workflows/functional-sharedfilesystems.yaml index 7d552435ef..5bf6f0b633 100644 --- a/.github/workflows/functional-sharedfilesystems.yaml +++ b/.github/workflows/functional-sharedfilesystems.yaml @@ -12,6 +12,9 @@ jobs: openstack_version: ["master"] ubuntu_version: ["20.04"] include: + - name: "antelope" + openstack_version: "stable/2023.1" + ubuntu_version: "22.04" - name: "zed" openstack_version: "stable/zed" ubuntu_version: "20.04" From 8126d0a86c7abf14c0db94d51775da62f967c47d Mon Sep 17 00:00:00 2001 From: Emilien Macchi <emacchi@redhat.com> Date: Tue, 5 Sep 2023 22:12:47 -0400 Subject: [PATCH 302/360] ci/functional: reduce LOC (cherry picked from commit e2a497e0624bf79d844ea5e3c6717fbb2982891c) --- .../workflows/functional-containerinfra.yaml | 14 +------------ .github/workflows/functional-dns.yaml | 14 +------------ .github/workflows/functional-networking.yaml | 21 ++----------------- 3 files changed, 4 insertions(+), 45 deletions(-) diff --git a/.github/workflows/functional-containerinfra.yaml b/.github/workflows/functional-containerinfra.yaml index 6332af01ec..a9d7f078dd 100644 --- a/.github/workflows/functional-containerinfra.yaml +++ b/.github/workflows/functional-containerinfra.yaml @@ -12,36 +12,24 @@ jobs: - name: "master" openstack_version: "master" ubuntu_version: "20.04" - devstack_conf_overrides: | - enable_plugin magnum https://github.com/openstack/magnum master - name: "antelope" openstack_version: "stable/2023.1" ubuntu_version: "22.04" - name: "zed" openstack_version: "stable/zed" ubuntu_version: "20.04" - devstack_conf_overrides: | - enable_plugin magnum https://github.com/openstack/magnum stable/zed - name: "yoga" openstack_version: "stable/yoga" ubuntu_version: "20.04" - devstack_conf_overrides: | - enable_plugin magnum https://github.com/openstack/magnum stable/yoga - name: "xena" openstack_version: "stable/xena" ubuntu_version: "20.04" - devstack_conf_overrides: | - enable_plugin magnum https://github.com/openstack/magnum stable/xena - name: "wallaby" openstack_version: "stable/wallaby" ubuntu_version: "20.04" - devstack_conf_overrides: | - enable_plugin magnum https://github.com/openstack/magnum stable/wallaby - name: "victoria" openstack_version: "stable/victoria" ubuntu_version: "20.04" - devstack_conf_overrides: | - enable_plugin magnum https://github.com/openstack/magnum stable/victoria runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Deploy OpenStack ${{ matrix.name }} with Magnum and run containerinfra acceptance tests steps: @@ -52,13 +40,13 @@ jobs: with: branch: ${{ matrix.openstack_version }} conf_overrides: | + enable_plugin magnum https://github.com/openstack/magnum ${{ matrix.openstack_version }} enable_plugin barbican https://github.com/openstack/barbican ${{ matrix.openstack_version }} enable_plugin heat https://github.com/openstack/heat ${{ matrix.openstack_version }} GLANCE_LIMIT_IMAGE_SIZE_TOTAL=5000 SWIFT_MAX_FILE_SIZE=5368709122 KEYSTONE_ADMIN_ENDPOINT=true MAGNUMCLIENT_BRANCH=${{ matrix.openstack_version }} - ${{ matrix.devstack_conf_overrides }} enabled_services: 'h-eng,h-api,h-api-cfn,h-api-cw' - name: Checkout go uses: actions/setup-go@v4 diff --git a/.github/workflows/functional-dns.yaml b/.github/workflows/functional-dns.yaml index dcca8b4c54..ae9bac3cd1 100644 --- a/.github/workflows/functional-dns.yaml +++ b/.github/workflows/functional-dns.yaml @@ -13,36 +13,24 @@ jobs: - name: "master" openstack_version: "master" ubuntu_version: "20.04" - devstack_conf_overrides: | - enable_plugin designate https://github.com/openstack/designate master - name: "antelope" openstack_version: "stable/2023.1" ubuntu_version: "22.04" - name: "zed" openstack_version: "stable/zed" ubuntu_version: "20.04" - devstack_conf_overrides: | - enable_plugin designate https://github.com/openstack/designate stable/zed - name: "yoga" openstack_version: "stable/yoga" ubuntu_version: "20.04" - devstack_conf_overrides: | - enable_plugin designate https://github.com/openstack/designate stable/yoga - name: "xena" openstack_version: "stable/xena" ubuntu_version: "20.04" - devstack_conf_overrides: | - enable_plugin designate https://github.com/openstack/designate stable/xena - name: "wallaby" openstack_version: "stable/wallaby" ubuntu_version: "20.04" - devstack_conf_overrides: | - enable_plugin designate https://github.com/openstack/designate stable/wallaby - name: "victoria" openstack_version: "stable/victoria" ubuntu_version: "20.04" - devstack_conf_overrides: | - enable_plugin designate https://github.com/openstack/designate stable/victoria runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Deploy OpenStack ${{ matrix.name }} with Designate and run dns acceptance tests steps: @@ -53,7 +41,7 @@ jobs: with: branch: ${{ matrix.openstack_version }} conf_overrides: | - ${{ matrix.devstack_conf_overrides }} + enable_plugin designate https://github.com/openstack/designate ${{ matrix.openstack_version }} enabled_services: 'designate,designate-central,designate-api,designate-worker,designate-producer,designate-mdns' - name: Checkout go uses: actions/setup-go@v4 diff --git a/.github/workflows/functional-networking.yaml b/.github/workflows/functional-networking.yaml index f8cb132a16..5531ee926c 100644 --- a/.github/workflows/functional-networking.yaml +++ b/.github/workflows/functional-networking.yaml @@ -12,42 +12,24 @@ jobs: - name: "master" openstack_version: "master" ubuntu_version: "20.04" - devstack_conf_overrides: | - enable_plugin neutron-dynamic-routing https://github.com/openstack/neutron-dynamic-routing master - enable_plugin neutron-vpnaas https://github.com/openstack/neutron-vpnaas master - name: "antelope" openstack_version: "stable/2023.1" ubuntu_version: "22.04" - name: "zed" openstack_version: "stable/zed" ubuntu_version: "20.04" - devstack_conf_overrides: | - enable_plugin neutron-dynamic-routing https://github.com/openstack/neutron-dynamic-routing stable/zed - enable_plugin neutron-vpnaas https://github.com/openstack/neutron-vpnaas stable/zed - name: "yoga" openstack_version: "stable/yoga" ubuntu_version: "20.04" - devstack_conf_overrides: | - enable_plugin neutron-dynamic-routing https://github.com/openstack/neutron-dynamic-routing stable/yoga - enable_plugin neutron-vpnaas https://github.com/openstack/neutron-vpnaas stable/yoga - name: "xena" openstack_version: "stable/xena" ubuntu_version: "20.04" - devstack_conf_overrides: | - enable_plugin neutron-dynamic-routing https://github.com/openstack/neutron-dynamic-routing stable/xena - enable_plugin neutron-vpnaas https://github.com/openstack/neutron-vpnaas stable/xena - name: "wallaby" openstack_version: "stable/wallaby" ubuntu_version: "20.04" - devstack_conf_overrides: | - enable_plugin neutron-dynamic-routing https://github.com/openstack/neutron-dynamic-routing stable/wallaby - enable_plugin neutron-vpnaas https://github.com/openstack/neutron-vpnaas stable/wallaby - name: "victoria" openstack_version: "stable/victoria" ubuntu_version: "20.04" - devstack_conf_overrides: | - enable_plugin neutron-dynamic-routing https://github.com/openstack/neutron-dynamic-routing stable/victoria - enable_plugin neutron-vpnaas https://github.com/openstack/neutron-vpnaas stable/victoria runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Deploy OpenStack ${{ matrix.name }} with Neutron and run networking acceptance tests steps: @@ -58,8 +40,9 @@ jobs: with: branch: ${{ matrix.openstack_version }} conf_overrides: | + enable_plugin neutron-dynamic-routing https://github.com/openstack/neutron-dynamic-routing ${{ matrix.openstack_version }} + enable_plugin neutron-vpnaas https://github.com/openstack/neutron-vpnaas ${{ matrix.openstack_version }} Q_ML2_PLUGIN_EXT_DRIVERS=qos,port_security,dns_domain_keywords - ${{ matrix.devstack_conf_overrides }} enabled_services: 'neutron-dns,neutron-qos,neutron-segments,neutron-trunk,neutron-uplink-status-propagation,neutron-network-segment-range,neutron-port-forwarding' - name: Checkout go uses: actions/setup-go@v4 From 5ac547f223590bac9a490027c3b8d110a4f9e91b Mon Sep 17 00:00:00 2001 From: Emilien Macchi <emacchi@redhat.com> Date: Tue, 5 Sep 2023 16:11:35 -0400 Subject: [PATCH 303/360] ci/functional: switch master jobs to run on ubuntu 22.04 Since https://github.com/openstack/devstack/commit/427a4e1a9b7f20a8be0ad5091f2229945ce711a8 We don't support focal anymore in devstack-master. We need to run 22.04 from now. Note: some workflows already had it right but it wasn't consistent across all services. (cherry picked from commit 9a8b0de0519b455afaf88ee38ee66f5babf8e3cf) --- .github/workflows/functional-baremetal.yaml | 2 +- .github/workflows/functional-basic.yaml | 2 +- .github/workflows/functional-blockstorage.yaml | 2 +- .github/workflows/functional-compute.yaml | 2 +- .github/workflows/functional-containerinfra.yaml | 2 +- .github/workflows/functional-dns.yaml | 2 +- .github/workflows/functional-identity.yaml | 2 +- .github/workflows/functional-imageservice.yaml | 2 +- .github/workflows/functional-keymanager.yaml | 2 +- .github/workflows/functional-loadbalancer.yaml | 2 +- .github/workflows/functional-networking.yaml | 2 +- .github/workflows/functional-objectstorage.yaml | 2 +- .github/workflows/functional-orchestration.yaml | 2 +- .github/workflows/functional-placement.yaml | 2 +- .github/workflows/functional-sharedfilesystems.yaml | 2 +- 15 files changed, 15 insertions(+), 15 deletions(-) diff --git a/.github/workflows/functional-baremetal.yaml b/.github/workflows/functional-baremetal.yaml index e3a55ad4f6..5002c08be0 100644 --- a/.github/workflows/functional-baremetal.yaml +++ b/.github/workflows/functional-baremetal.yaml @@ -10,7 +10,7 @@ jobs: matrix: name: ["master"] openstack_version: ["master"] - ubuntu_version: ["20.04"] + ubuntu_version: ["22.04"] include: - name: "antelope" openstack_version: "stable/2023.1" diff --git a/.github/workflows/functional-basic.yaml b/.github/workflows/functional-basic.yaml index 93f412f346..17225e1e5f 100644 --- a/.github/workflows/functional-basic.yaml +++ b/.github/workflows/functional-basic.yaml @@ -13,7 +13,7 @@ jobs: matrix: name: ["master"] openstack_version: ["master"] - ubuntu_version: ["20.04"] + ubuntu_version: ["22.04"] include: - name: "antelope" openstack_version: "stable/2023.1" diff --git a/.github/workflows/functional-blockstorage.yaml b/.github/workflows/functional-blockstorage.yaml index ed6fb9187f..4b8f69133e 100644 --- a/.github/workflows/functional-blockstorage.yaml +++ b/.github/workflows/functional-blockstorage.yaml @@ -10,7 +10,7 @@ jobs: matrix: name: ["master"] openstack_version: ["master"] - ubuntu_version: ["20.04"] + ubuntu_version: ["22.04"] include: - name: "antelope" openstack_version: "stable/2023.1" diff --git a/.github/workflows/functional-compute.yaml b/.github/workflows/functional-compute.yaml index 15ab332bd2..65311bce6e 100644 --- a/.github/workflows/functional-compute.yaml +++ b/.github/workflows/functional-compute.yaml @@ -10,7 +10,7 @@ jobs: matrix: name: ["master"] openstack_version: ["master"] - ubuntu_version: ["20.04"] + ubuntu_version: ["22.04"] include: - name: "antelope" openstack_version: "stable/2023.1" diff --git a/.github/workflows/functional-containerinfra.yaml b/.github/workflows/functional-containerinfra.yaml index a9d7f078dd..cbdbb5cce1 100644 --- a/.github/workflows/functional-containerinfra.yaml +++ b/.github/workflows/functional-containerinfra.yaml @@ -11,7 +11,7 @@ jobs: include: - name: "master" openstack_version: "master" - ubuntu_version: "20.04" + ubuntu_version: "22.04" - name: "antelope" openstack_version: "stable/2023.1" ubuntu_version: "22.04" diff --git a/.github/workflows/functional-dns.yaml b/.github/workflows/functional-dns.yaml index ae9bac3cd1..e332d273ae 100644 --- a/.github/workflows/functional-dns.yaml +++ b/.github/workflows/functional-dns.yaml @@ -12,7 +12,7 @@ jobs: include: - name: "master" openstack_version: "master" - ubuntu_version: "20.04" + ubuntu_version: "22.04" - name: "antelope" openstack_version: "stable/2023.1" ubuntu_version: "22.04" diff --git a/.github/workflows/functional-identity.yaml b/.github/workflows/functional-identity.yaml index ee510977ae..3170e41e06 100644 --- a/.github/workflows/functional-identity.yaml +++ b/.github/workflows/functional-identity.yaml @@ -10,7 +10,7 @@ jobs: matrix: name: ["master"] openstack_version: ["master"] - ubuntu_version: ["20.04"] + ubuntu_version: ["22.04"] include: - name: "antelope" openstack_version: "stable/2023.1" diff --git a/.github/workflows/functional-imageservice.yaml b/.github/workflows/functional-imageservice.yaml index 18bb18ae63..744ee93a05 100644 --- a/.github/workflows/functional-imageservice.yaml +++ b/.github/workflows/functional-imageservice.yaml @@ -10,7 +10,7 @@ jobs: matrix: name: ["master"] openstack_version: ["master"] - ubuntu_version: ["20.04"] + ubuntu_version: ["22.04"] include: - name: "antelope" openstack_version: "stable/2023.1" diff --git a/.github/workflows/functional-keymanager.yaml b/.github/workflows/functional-keymanager.yaml index 9865935091..8d72e06a60 100644 --- a/.github/workflows/functional-keymanager.yaml +++ b/.github/workflows/functional-keymanager.yaml @@ -10,7 +10,7 @@ jobs: matrix: name: ["master"] openstack_version: ["master"] - ubuntu_version: ["20.04"] + ubuntu_version: ["22.04"] include: - name: "antelope" openstack_version: "stable/2023.1" diff --git a/.github/workflows/functional-loadbalancer.yaml b/.github/workflows/functional-loadbalancer.yaml index 142f8f10a4..9af476b3a5 100644 --- a/.github/workflows/functional-loadbalancer.yaml +++ b/.github/workflows/functional-loadbalancer.yaml @@ -10,7 +10,7 @@ jobs: matrix: name: ["master"] openstack_version: ["master"] - ubuntu_version: ["20.04"] + ubuntu_version: ["22.04"] include: - name: "antelope" openstack_version: "stable/2023.1" diff --git a/.github/workflows/functional-networking.yaml b/.github/workflows/functional-networking.yaml index 5531ee926c..0a33e5787c 100644 --- a/.github/workflows/functional-networking.yaml +++ b/.github/workflows/functional-networking.yaml @@ -11,7 +11,7 @@ jobs: include: - name: "master" openstack_version: "master" - ubuntu_version: "20.04" + ubuntu_version: "22.04" - name: "antelope" openstack_version: "stable/2023.1" ubuntu_version: "22.04" diff --git a/.github/workflows/functional-objectstorage.yaml b/.github/workflows/functional-objectstorage.yaml index 1625993a34..bf7e4f5b6f 100644 --- a/.github/workflows/functional-objectstorage.yaml +++ b/.github/workflows/functional-objectstorage.yaml @@ -10,7 +10,7 @@ jobs: matrix: name: ["master"] openstack_version: ["master"] - ubuntu_version: ["20.04"] + ubuntu_version: ["22.04"] include: - name: "antelope" openstack_version: "stable/2023.1" diff --git a/.github/workflows/functional-orchestration.yaml b/.github/workflows/functional-orchestration.yaml index 730c40f79b..5f81b3e733 100644 --- a/.github/workflows/functional-orchestration.yaml +++ b/.github/workflows/functional-orchestration.yaml @@ -10,7 +10,7 @@ jobs: matrix: name: ["master"] openstack_version: ["master"] - ubuntu_version: ["20.04"] + ubuntu_version: ["22.04"] include: - name: "antelope" openstack_version: "stable/2023.1" diff --git a/.github/workflows/functional-placement.yaml b/.github/workflows/functional-placement.yaml index 02422329e7..a7efecdd24 100644 --- a/.github/workflows/functional-placement.yaml +++ b/.github/workflows/functional-placement.yaml @@ -10,7 +10,7 @@ jobs: matrix: name: ["master"] openstack_version: ["master"] - ubuntu_version: ["20.04"] + ubuntu_version: ["22.04"] include: - name: "antelope" openstack_version: "stable/2023.1" diff --git a/.github/workflows/functional-sharedfilesystems.yaml b/.github/workflows/functional-sharedfilesystems.yaml index 5bf6f0b633..48acec333e 100644 --- a/.github/workflows/functional-sharedfilesystems.yaml +++ b/.github/workflows/functional-sharedfilesystems.yaml @@ -10,7 +10,7 @@ jobs: matrix: name: ["master"] openstack_version: ["master"] - ubuntu_version: ["20.04"] + ubuntu_version: ["22.04"] include: - name: "antelope" openstack_version: "stable/2023.1" From 2aab79dd3a0fd19082f9f211f126ee86c17652fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= <m.andre@redhat.com> Date: Thu, 7 Sep 2023 15:06:57 +0200 Subject: [PATCH 304/360] Acceptance: Rename IsReleasesBelow() and IsReleasesAbove() Rename to IsCurrentBelow() and IsCurrentAbove() respectively. --- acceptance/clients/conditions.go | 12 ++++++------ acceptance/openstack/loadbalancer/v2/loadbalancer.go | 2 +- acceptance/openstack/loadbalancer/v2/quotas_test.go | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/acceptance/clients/conditions.go b/acceptance/clients/conditions.go index 44522c2308..6308eaeac6 100644 --- a/acceptance/clients/conditions.go +++ b/acceptance/clients/conditions.go @@ -111,7 +111,7 @@ func SkipRelease(t *testing.T, release string) { func SkipReleasesBelow(t *testing.T, release string) { current_branch := getReleaseFromEnv(t) - if IsReleasesBelow(t, release) { + if IsCurrentBelow(t, release) { t.Skipf("this is not supported below %s, testing in %s", release, current_branch) } } @@ -123,15 +123,15 @@ func SkipReleasesAbove(t *testing.T, release string) { current_branch := getReleaseFromEnv(t) // Assume master is always too new - if IsReleasesAbove(t, release) { + if IsCurrentAbove(t, release) { t.Skipf("this is not supported above %s, testing in %s", release, current_branch) } } -// IsReleasesAbove will return true on releases above a certain +// IsCurrentAbove will return true on releases above a certain // one. The result is always true on master release. Releases are named such // as 'stable/mitaka', master, etc. -func IsReleasesAbove(t *testing.T, release string) bool { +func IsCurrentAbove(t *testing.T, release string) bool { current_branch := getReleaseFromEnv(t) // Assume master is always too new @@ -142,9 +142,9 @@ func IsReleasesAbove(t *testing.T, release string) bool { return false } -// IsReleasesBelow will return true on releases below a certain +// IsCurrentBelow will return true on releases below a certain // one. Releases are named such as 'stable/mitaka', master, etc. -func IsReleasesBelow(t *testing.T, release string) bool { +func IsCurrentBelow(t *testing.T, release string) bool { current_branch := getReleaseFromEnv(t) if current_branch != "master" || current_branch < release { diff --git a/acceptance/openstack/loadbalancer/v2/loadbalancer.go b/acceptance/openstack/loadbalancer/v2/loadbalancer.go index 8d5efb011d..eb11d68c17 100644 --- a/acceptance/openstack/loadbalancer/v2/loadbalancer.go +++ b/acceptance/openstack/loadbalancer/v2/loadbalancer.go @@ -71,7 +71,7 @@ func CreateListenerHTTP(t *testing.T, client *gophercloud.ServiceClient, lb *loa } // tls_version is only supported in microversion v2.17 introduced in victoria - if clients.IsReleasesAbove(t, "stable/ussuri") { + if clients.IsCurrentAbove(t, "stable/ussuri") { tlsVersions = []listeners.TLSVersion{"TLSv1.2", "TLSv1.3"} tlsVersionsExp = []string{"TLSv1.2", "TLSv1.3"} } diff --git a/acceptance/openstack/loadbalancer/v2/quotas_test.go b/acceptance/openstack/loadbalancer/v2/quotas_test.go index 4e174642c4..35dcb2e54a 100644 --- a/acceptance/openstack/loadbalancer/v2/quotas_test.go +++ b/acceptance/openstack/loadbalancer/v2/quotas_test.go @@ -45,7 +45,7 @@ func TestQuotasUpdate(t *testing.T) { Healthmonitor: gophercloud.IntToPointer(5), } // L7 parameters are only supported in microversion v2.19 introduced in victoria - if clients.IsReleasesAbove(t, "stable/ussuri") { + if clients.IsCurrentAbove(t, "stable/ussuri") { quotaUpdateOpts.L7Policy = gophercloud.IntToPointer(55) quotaUpdateOpts.L7Rule = gophercloud.IntToPointer(105) } @@ -67,7 +67,7 @@ func TestQuotasUpdate(t *testing.T) { Healthmonitor: &originalQuotas.Healthmonitor, } // L7 parameters are only supported in microversion v2.19 introduced in victoria - if clients.IsReleasesAbove(t, "stable/ussuri") { + if clients.IsCurrentAbove(t, "stable/ussuri") { restoredQuotaUpdate.L7Policy = &originalQuotas.L7Policy restoredQuotaUpdate.L7Rule = &originalQuotas.L7Rule } From e5568fea158497f6b34714d2cead81d2b96ce15a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= <m.andre@redhat.com> Date: Thu, 7 Sep 2023 18:07:51 +0200 Subject: [PATCH 305/360] Acceptance: Fix IsCurrentBelow and IsCurrentAbove functions Take into account numerical release names, after zed, and ensure that comparing the same version always yields false. --- acceptance/clients/conditions.go | 73 +++++++++++++------ acceptance/clients/testing/conditions_test.go | 65 +++++++++++++++++ 2 files changed, 115 insertions(+), 23 deletions(-) create mode 100644 acceptance/clients/testing/conditions_test.go diff --git a/acceptance/clients/conditions.go b/acceptance/clients/conditions.go index 6308eaeac6..a1239a1ba7 100644 --- a/acceptance/clients/conditions.go +++ b/acceptance/clients/conditions.go @@ -2,6 +2,8 @@ package clients import ( "os" + "strconv" + "strings" "testing" ) @@ -90,18 +92,18 @@ func RequireIronicHTTPBasic(t *testing.T) { } func getReleaseFromEnv(t *testing.T) string { - current_branch := os.Getenv("OS_BRANCH") - if current_branch == "" { + current := strings.TrimPrefix(os.Getenv("OS_BRANCH"), "stable/") + if current == "" { t.Fatal("this test requires OS_BRANCH to be set but it wasn't") } - return current_branch + return current } // SkipRelease will have the test be skipped on a certain // release. Releases are named such as 'stable/mitaka', master, etc. func SkipRelease(t *testing.T, release string) { - current_branch := getReleaseFromEnv(t) - if current_branch == release { + current := getReleaseFromEnv(t) + if current == release { t.Skipf("this is not supported in %s", release) } } @@ -109,10 +111,10 @@ func SkipRelease(t *testing.T, release string) { // SkipReleasesBelow will have the test be skipped on releases below a certain // one. Releases are named such as 'stable/mitaka', master, etc. func SkipReleasesBelow(t *testing.T, release string) { - current_branch := getReleaseFromEnv(t) + current := getReleaseFromEnv(t) if IsCurrentBelow(t, release) { - t.Skipf("this is not supported below %s, testing in %s", release, current_branch) + t.Skipf("this is not supported below %s, testing in %s", release, current) } } @@ -120,36 +122,61 @@ func SkipReleasesBelow(t *testing.T, release string) { // one. The test is always skipped on master release. Releases are named such // as 'stable/mitaka', master, etc. func SkipReleasesAbove(t *testing.T, release string) { - current_branch := getReleaseFromEnv(t) + current := getReleaseFromEnv(t) - // Assume master is always too new if IsCurrentAbove(t, release) { - t.Skipf("this is not supported above %s, testing in %s", release, current_branch) + t.Skipf("this is not supported above %s, testing in %s", release, current) } } +func isReleaseNumeral(release string) bool { + _, err := strconv.Atoi(release[0:1]) + return err == nil +} + // IsCurrentAbove will return true on releases above a certain // one. The result is always true on master release. Releases are named such // as 'stable/mitaka', master, etc. func IsCurrentAbove(t *testing.T, release string) bool { - current_branch := getReleaseFromEnv(t) - - // Assume master is always too new - if current_branch == "master" || current_branch > release { - return true - } - t.Logf("Target release %s is below the current branch %s", release, current_branch) + current := getReleaseFromEnv(t) + release = strings.TrimPrefix(release, "stable/") + + if release != "master" { + // Assume master is always too new + if current == "master" { + return true + } + // Numeral releases are always newer than non-numeral ones + if isReleaseNumeral(current) && !isReleaseNumeral(release) { + return true + } + if current > release && !(!isReleaseNumeral(current) && isReleaseNumeral(release)) { + return true + } + } + t.Logf("Target release %s is below the current branch %s", release, current) return false } // IsCurrentBelow will return true on releases below a certain // one. Releases are named such as 'stable/mitaka', master, etc. func IsCurrentBelow(t *testing.T, release string) bool { - current_branch := getReleaseFromEnv(t) - - if current_branch != "master" || current_branch < release { - return true - } - t.Logf("Target release %s is above the current branch %s", release, current_branch) + current := getReleaseFromEnv(t) + release = strings.TrimPrefix(release, "stable/") + + if current != "master" { + // Assume master is always too new + if release == "master" { + return true + } + // Numeral releases are always newer than non-numeral ones + if isReleaseNumeral(release) && !isReleaseNumeral(current) { + return true + } + if release > current && !(!isReleaseNumeral(release) && isReleaseNumeral(current)) { + return true + } + } + t.Logf("Target release %s is above the current branch %s", release, current) return false } diff --git a/acceptance/clients/testing/conditions_test.go b/acceptance/clients/testing/conditions_test.go new file mode 100644 index 0000000000..a9825ea293 --- /dev/null +++ b/acceptance/clients/testing/conditions_test.go @@ -0,0 +1,65 @@ +package testing + +import ( + "fmt" + "os" + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" +) + +func TestIsCurrentAbove(t *testing.T) { + cases := []struct { + Current string + Release string + Result bool + }{ + {Current: "master", Release: "zed", Result: true}, + {Current: "master", Release: "2023.1", Result: true}, + {Current: "master", Release: "master", Result: false}, + {Current: "zed", Release: "master", Result: false}, + {Current: "zed", Release: "yoga", Result: true}, + {Current: "zed", Release: "2023.1", Result: false}, + {Current: "2023.1", Release: "2023.1", Result: false}, + {Current: "2023.2", Release: "stable/2023.1", Result: true}, + } + + for _, tt := range cases { + t.Run(fmt.Sprintf("%s above %s", tt.Current, tt.Release), func(t *testing.T) { + os.Setenv("OS_BRANCH", tt.Current) + got := clients.IsCurrentAbove(t, tt.Release) + if got != tt.Result { + t.Errorf("got %v want %v", got, tt.Result) + } + }) + + } +} + +func TestIsCurrentBelow(t *testing.T) { + cases := []struct { + Current string + Release string + Result bool + }{ + {Current: "master", Release: "zed", Result: false}, + {Current: "master", Release: "2023.1", Result: false}, + {Current: "master", Release: "master", Result: false}, + {Current: "zed", Release: "master", Result: true}, + {Current: "zed", Release: "yoga", Result: false}, + {Current: "zed", Release: "2023.1", Result: true}, + {Current: "2023.1", Release: "2023.1", Result: false}, + {Current: "2023.2", Release: "stable/2023.1", Result: false}, + } + + for _, tt := range cases { + t.Run(fmt.Sprintf("%s below %s", tt.Current, tt.Release), func(t *testing.T) { + os.Setenv("OS_BRANCH", tt.Current) + got := clients.IsCurrentBelow(t, tt.Release) + if got != tt.Result { + t.Errorf("got %v want %v", got, tt.Result) + } + }) + + } +} From d7d4aac9a96aec29c746dc80a0a4292c9da3967c Mon Sep 17 00:00:00 2001 From: Emilien Macchi <emacchi@redhat.com> Date: Thu, 7 Sep 2023 14:22:28 -0400 Subject: [PATCH 306/360] ci/semver-auto: return a failure only when needed When go-apidiff returns semver=major, it returns RC=1 which causes the workflow to return an error while we don't want that, we just want the label to be applied. Instead, let's ignore that error so the label can be set later and add a step to check if an error occured and semver is not major, then it's an actual problem that we should check in the logs (e.g. an issue when running go-apidiff). (cherry picked from commit 12ff5ebd8f619dfb58968234568fb0ff8d14ea71) --- .github/workflows/semver-auto.yaml | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/.github/workflows/semver-auto.yaml b/.github/workflows/semver-auto.yaml index 2aa66b83fd..edc3c6dcf2 100644 --- a/.github/workflows/semver-auto.yaml +++ b/.github/workflows/semver-auto.yaml @@ -43,24 +43,33 @@ jobs: - name: Checking Go API Compatibility id: go-apidiff + # if semver=major, this will return RC=1, so let's ignore the failure so label + # can be set later. We check for actual errors in the next step. + continue-on-error: true uses: joelanford/go-apidiff@v0.7.0 + # go-apidiff returns RC=1 when semver=major, which makes the workflow to return + # a failure. Instead let's just return a failure if go-apidiff failed to run. + - name: Return an error if Go API Compatibility couldn't be verified + if: steps.go-apidiff.outcome != 'success' && steps.go-apidiff.semver-type != 'major' + run: exit 1 + - name: Add semver:patch label - if: always() && steps.go-apidiff.outputs.semver-type == 'patch' + if: steps.go-apidiff.outputs.semver-type == 'patch' uses: actions-ecosystem/action-add-labels@18f1af5e3544586314bbe15c0273249c770b2daf with: github_token: ${{ secrets.GITHUB_TOKEN }} labels: semver:patch - name: Add semver:minor label - if: always() && steps.go-apidiff.outputs.semver-type == 'minor' + if: steps.go-apidiff.outputs.semver-type == 'minor' uses: actions-ecosystem/action-add-labels@18f1af5e3544586314bbe15c0273249c770b2daf with: github_token: ${{ secrets.GITHUB_TOKEN }} labels: semver:minor - name: Add semver:major label - if: always() && steps.go-apidiff.outputs.semver-type == 'major' + if: steps.go-apidiff.outputs.semver-type == 'major' uses: actions-ecosystem/action-add-labels@18f1af5e3544586314bbe15c0273249c770b2daf with: github_token: ${{ secrets.GITHUB_TOKEN }} From 27c2a3f1408b6e33e29adea09323a9da5724aeda Mon Sep 17 00:00:00 2001 From: Emilien Macchi <emacchi@redhat.com> Date: Fri, 8 Sep 2023 09:15:40 -0400 Subject: [PATCH 307/360] Fix semver-auto In my previous commit, I didn't read the output, fixing now. (cherry picked from commit 2999b21e92d1033d01ccc10e5f9b06458dacb450) --- .github/workflows/semver-auto.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/semver-auto.yaml b/.github/workflows/semver-auto.yaml index edc3c6dcf2..88a6ca9dc2 100644 --- a/.github/workflows/semver-auto.yaml +++ b/.github/workflows/semver-auto.yaml @@ -51,7 +51,7 @@ jobs: # go-apidiff returns RC=1 when semver=major, which makes the workflow to return # a failure. Instead let's just return a failure if go-apidiff failed to run. - name: Return an error if Go API Compatibility couldn't be verified - if: steps.go-apidiff.outcome != 'success' && steps.go-apidiff.semver-type != 'major' + if: steps.go-apidiff.outcome != 'success' && steps.go-apidiff.outputs.semver-type != 'major' run: exit 1 - name: Add semver:patch label From 5871a113d777c53dc5cbe8d98e5fea8fd6eaf8b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= <m.andre@redhat.com> Date: Fri, 22 Sep 2023 15:46:35 +0200 Subject: [PATCH 308/360] Make acceptance tests internal Do not expose acceptance tests packages externally. This has the added benefit of go-apidiff no longer reporting false positive when analyzing for backward incompatible changes. --- .github/workflows/functional-basic.yaml | 2 +- {acceptance => internal/acceptance}/README.md | 2 +- {acceptance => internal/acceptance}/clients/clients.go | 0 .../acceptance}/clients/conditions.go | 0 {acceptance => internal/acceptance}/clients/http.go | 0 .../acceptance}/clients/testing/conditions_test.go | 2 +- .../openstack/baremetal/httpbasic/allocations_test.go | 4 ++-- .../acceptance}/openstack/baremetal/httpbasic/doc.go | 0 .../openstack/baremetal/httpbasic/nodes_test.go | 4 ++-- .../openstack/baremetal/httpbasic/ports_test.go | 4 ++-- .../openstack/baremetal/noauth/allocations_test.go | 4 ++-- .../acceptance}/openstack/baremetal/noauth/doc.go | 0 .../openstack/baremetal/noauth/nodes_test.go | 4 ++-- .../openstack/baremetal/noauth/ports_test.go | 4 ++-- .../openstack/baremetal/v1/allocations_test.go | 2 +- .../acceptance}/openstack/baremetal/v1/baremetal.go | 2 +- .../openstack/baremetal/v1/conductors_test.go | 4 ++-- .../acceptance}/openstack/baremetal/v1/nodes_test.go | 2 +- .../acceptance}/openstack/baremetal/v1/ports_test.go | 2 +- .../openstack/blockstorage/apiversions_test.go | 4 ++-- .../openstack/blockstorage/extensions/backups_test.go | 4 ++-- .../openstack/blockstorage/extensions/extensions.go | 2 +- .../openstack/blockstorage/extensions/limits_test.go | 4 ++-- .../openstack/blockstorage/extensions/pkg.go | 0 .../blockstorage/extensions/schedulerhints_test.go | 4 ++-- .../blockstorage/extensions/schedulerstats_test.go | 4 ++-- .../openstack/blockstorage/extensions/services_test.go | 4 ++-- .../blockstorage/extensions/volumeactions_test.go | 10 +++++----- .../blockstorage/extensions/volumetenants_test.go | 4 ++-- .../openstack/blockstorage/noauth/blockstorage.go | 4 ++-- .../openstack/blockstorage/noauth/snapshots_test.go | 4 ++-- .../openstack/blockstorage/noauth/volumes_test.go | 4 ++-- .../openstack/blockstorage/v1/blockstorage.go | 2 +- .../acceptance}/openstack/blockstorage/v1/pkg.go | 0 .../openstack/blockstorage/v1/snapshots_test.go | 4 ++-- .../openstack/blockstorage/v1/volumes_test.go | 4 ++-- .../openstack/blockstorage/v1/volumetypes_test.go | 4 ++-- .../openstack/blockstorage/v2/blockstorage.go | 4 ++-- .../acceptance}/openstack/blockstorage/v2/pkg.go | 0 .../openstack/blockstorage/v2/snapshots_test.go | 4 ++-- .../openstack/blockstorage/v2/volumes_test.go | 4 ++-- .../openstack/blockstorage/v3/blockstorage.go | 2 +- .../acceptance}/openstack/blockstorage/v3/pkg.go | 0 .../acceptance}/openstack/blockstorage/v3/qos_test.go | 4 ++-- .../openstack/blockstorage/v3/quotaset_test.go | 4 ++-- .../openstack/blockstorage/v3/snapshots_test.go | 4 ++-- .../openstack/blockstorage/v3/volumeattachments.go | 0 .../blockstorage/v3/volumeattachments_test.go | 4 ++-- .../openstack/blockstorage/v3/volumes_test.go | 4 ++-- .../openstack/blockstorage/v3/volumetypes_test.go | 6 +++--- .../acceptance}/openstack/client_test.go | 4 ++-- .../openstack/clustering/v1/actions_test.go | 4 ++-- .../acceptance}/openstack/clustering/v1/clustering.go | 4 ++-- .../openstack/clustering/v1/clusters_test.go | 4 ++-- .../acceptance}/openstack/clustering/v1/events_test.go | 4 ++-- .../acceptance}/openstack/clustering/v1/nodes_test.go | 4 ++-- .../acceptance}/openstack/clustering/v1/pkg.go | 0 .../openstack/clustering/v1/policies_test.go | 4 ++-- .../openstack/clustering/v1/policytypes_test.go | 4 ++-- .../openstack/clustering/v1/profiles_test.go | 4 ++-- .../openstack/clustering/v1/profiletypes_test.go | 4 ++-- .../openstack/clustering/v1/receivers_test.go | 4 ++-- .../openstack/clustering/v1/webhooktrigger_test.go | 2 +- .../acceptance}/openstack/common.go | 0 .../openstack/compute/v2/aggregates_test.go | 4 ++-- .../openstack/compute/v2/attachinterfaces_test.go | 4 ++-- .../openstack/compute/v2/availabilityzones_test.go | 4 ++-- .../openstack/compute/v2/bootfromvolume_test.go | 6 +++--- .../acceptance}/openstack/compute/v2/compute.go | 4 ++-- .../openstack/compute/v2/defsecrules_test.go | 4 ++-- .../openstack/compute/v2/diagnostics_test.go | 4 ++-- .../acceptance}/openstack/compute/v2/extension_test.go | 4 ++-- .../acceptance}/openstack/compute/v2/flavors_test.go | 6 +++--- .../openstack/compute/v2/floatingip_test.go | 4 ++-- .../openstack/compute/v2/hypervisors_test.go | 4 ++-- .../acceptance}/openstack/compute/v2/images_test.go | 4 ++-- .../openstack/compute/v2/instance_actions_test.go | 4 ++-- .../acceptance}/openstack/compute/v2/keypairs_test.go | 6 +++--- .../acceptance}/openstack/compute/v2/limits_test.go | 4 ++-- .../acceptance}/openstack/compute/v2/migrate_test.go | 2 +- .../acceptance}/openstack/compute/v2/network_test.go | 4 ++-- .../acceptance}/openstack/compute/v2/pkg.go | 0 .../acceptance}/openstack/compute/v2/quotaset_test.go | 4 ++-- .../openstack/compute/v2/remoteconsoles_test.go | 4 ++-- .../openstack/compute/v2/rescueunrescue_test.go | 2 +- .../acceptance}/openstack/compute/v2/secgroup_test.go | 4 ++-- .../openstack/compute/v2/servergroup_test.go | 4 ++-- .../acceptance}/openstack/compute/v2/servers_test.go | 6 +++--- .../acceptance}/openstack/compute/v2/services_test.go | 4 ++-- .../openstack/compute/v2/tenantnetworks_test.go | 4 ++-- .../acceptance}/openstack/compute/v2/usage_test.go | 4 ++-- .../openstack/compute/v2/volumeattach_test.go | 6 +++--- .../acceptance}/openstack/container/v1/capsules.go | 2 +- .../openstack/container/v1/capsules_test.go | 2 +- .../acceptance}/openstack/container/v1/fixtures.go | 0 .../openstack/containerinfra/v1/certificates_test.go | 2 +- .../openstack/containerinfra/v1/clusters_test.go | 4 ++-- .../containerinfra/v1/clustertemplates_test.go | 4 ++-- .../openstack/containerinfra/v1/containerinfra.go | 6 +++--- .../openstack/containerinfra/v1/nodegroups_test.go | 4 ++-- .../acceptance}/openstack/containerinfra/v1/pkg.go | 0 .../openstack/containerinfra/v1/quotas_test.go | 4 ++-- .../acceptance}/openstack/db/v1/configurations_test.go | 4 ++-- .../acceptance}/openstack/db/v1/databases_test.go | 4 ++-- .../acceptance}/openstack/db/v1/db.go | 4 ++-- .../acceptance}/openstack/db/v1/flavors_test.go | 4 ++-- .../acceptance}/openstack/db/v1/instances_test.go | 4 ++-- .../acceptance}/openstack/db/v1/pkg.go | 0 .../acceptance}/openstack/db/v1/users_test.go | 4 ++-- .../acceptance}/openstack/dns/v2/dns.go | 2 +- .../acceptance}/openstack/dns/v2/recordsets_test.go | 4 ++-- .../acceptance}/openstack/dns/v2/transfers_test.go | 6 +++--- .../acceptance}/openstack/dns/v2/zones_test.go | 4 ++-- .../openstack/identity/v2/extension_test.go | 4 ++-- .../acceptance}/openstack/identity/v2/identity.go | 2 +- .../acceptance}/openstack/identity/v2/pkg.go | 0 .../acceptance}/openstack/identity/v2/role_test.go | 4 ++-- .../acceptance}/openstack/identity/v2/tenant_test.go | 4 ++-- .../acceptance}/openstack/identity/v2/token_test.go | 4 ++-- .../acceptance}/openstack/identity/v2/user_test.go | 4 ++-- .../identity/v3/applicationcredentials_test.go | 4 ++-- .../acceptance}/openstack/identity/v3/catalog_test.go | 4 ++-- .../openstack/identity/v3/credentials_test.go | 4 ++-- .../acceptance}/openstack/identity/v3/domains_test.go | 4 ++-- .../openstack/identity/v3/ec2credentials_test.go | 4 ++-- .../acceptance}/openstack/identity/v3/endpoint_test.go | 4 ++-- .../openstack/identity/v3/federation_test.go | 4 ++-- .../acceptance}/openstack/identity/v3/groups_test.go | 4 ++-- .../acceptance}/openstack/identity/v3/identity.go | 2 +- .../acceptance}/openstack/identity/v3/limits_test.go | 4 ++-- .../acceptance}/openstack/identity/v3/oauth1_test.go | 4 ++-- .../openstack/identity/v3/osinherit_test.go | 2 +- .../acceptance}/openstack/identity/v3/pkg.go | 0 .../acceptance}/openstack/identity/v3/policies_test.go | 4 ++-- .../openstack/identity/v3/projectendpoint_test.go | 4 ++-- .../acceptance}/openstack/identity/v3/projects_test.go | 4 ++-- .../acceptance}/openstack/identity/v3/reauth_test.go | 2 +- .../acceptance}/openstack/identity/v3/regions_test.go | 4 ++-- .../openstack/identity/v3/registeredlimits_test.go | 4 ++-- .../acceptance}/openstack/identity/v3/roles_test.go | 4 ++-- .../acceptance}/openstack/identity/v3/service_test.go | 4 ++-- .../acceptance}/openstack/identity/v3/token_test.go | 4 ++-- .../acceptance}/openstack/identity/v3/trusts_test.go | 4 ++-- .../acceptance}/openstack/identity/v3/users_test.go | 4 ++-- .../openstack/imageservice/v2/imagedata_test.go | 4 ++-- .../openstack/imageservice/v2/imageimport_test.go | 4 ++-- .../openstack/imageservice/v2/images_test.go | 4 ++-- .../openstack/imageservice/v2/imageservice.go | 2 +- .../openstack/imageservice/v2/tasks_test.go | 4 ++-- .../acceptance}/openstack/keymanager/v1/acls_test.go | 4 ++-- .../openstack/keymanager/v1/containers_test.go | 4 ++-- .../acceptance}/openstack/keymanager/v1/keymanager.go | 2 +- .../acceptance}/openstack/keymanager/v1/orders_test.go | 4 ++-- .../openstack/keymanager/v1/secrets_test.go | 4 ++-- .../openstack/loadbalancer/v2/amphorae_test.go | 4 ++-- .../openstack/loadbalancer/v2/l7policies_test.go | 4 ++-- .../openstack/loadbalancer/v2/listeners_test.go | 4 ++-- .../openstack/loadbalancer/v2/loadbalancer.go | 4 ++-- .../openstack/loadbalancer/v2/loadbalancers_test.go | 8 ++++---- .../openstack/loadbalancer/v2/monitors_test.go | 4 ++-- .../acceptance}/openstack/loadbalancer/v2/pkg.go | 0 .../openstack/loadbalancer/v2/pools_test.go | 4 ++-- .../openstack/loadbalancer/v2/providers_test.go | 4 ++-- .../openstack/loadbalancer/v2/quotas_test.go | 4 ++-- .../acceptance}/openstack/messaging/v2/claims_test.go | 4 ++-- .../acceptance}/openstack/messaging/v2/message_test.go | 4 ++-- .../acceptance}/openstack/messaging/v2/messaging.go | 2 +- .../acceptance}/openstack/messaging/v2/queue_test.go | 4 ++-- .../openstack/networking/v2/apiversion_test.go | 4 ++-- .../openstack/networking/v2/extension_test.go | 4 ++-- .../networking/v2/extensions/agents/agents_test.go | 8 ++++---- .../openstack/networking/v2/extensions/agents/doc.go | 0 .../networking/v2/extensions/attributestags_test.go | 6 +++--- .../v2/extensions/bgp/peers/bgppeers_test.go | 4 ++-- .../networking/v2/extensions/bgp/peers/doc.go | 0 .../networking/v2/extensions/bgp/peers/peers.go | 2 +- .../v2/extensions/bgp/speakers/bgpspeakers_test.go | 8 ++++---- .../networking/v2/extensions/bgp/speakers/doc.go | 0 .../networking/v2/extensions/bgp/speakers/speakers.go | 2 +- .../openstack/networking/v2/extensions/dns/dns.go | 2 +- .../openstack/networking/v2/extensions/dns/dns_test.go | 8 ++++---- .../openstack/networking/v2/extensions/extensions.go | 2 +- .../networking/v2/extensions/fwaas/firewall_test.go | 6 +++--- .../openstack/networking/v2/extensions/fwaas/fwaas.go | 2 +- .../networking/v2/extensions/fwaas/policy_test.go | 4 ++-- .../networking/v2/extensions/fwaas/rule_test.go | 4 ++-- .../networking/v2/extensions/fwaas_v2/fwaas_v2.go | 2 +- .../networking/v2/extensions/fwaas_v2/groups_test.go | 4 ++-- .../networking/v2/extensions/fwaas_v2/policy_test.go | 4 ++-- .../networking/v2/extensions/fwaas_v2/rule_test.go | 4 ++-- .../v2/extensions/layer3/addressscopes_test.go | 4 ++-- .../v2/extensions/layer3/extraroutes_test.go | 6 +++--- .../v2/extensions/layer3/floatingips_test.go | 6 +++--- .../v2/extensions/layer3/l3_scheduling_test.go | 6 +++--- .../networking/v2/extensions/layer3/layer3.go | 4 ++-- .../v2/extensions/layer3/portforwardings_test.go | 6 +++--- .../networking/v2/extensions/layer3/routers_test.go | 6 +++--- .../openstack/networking/v2/extensions/lbaas/lbaas.go | 2 +- .../networking/v2/extensions/lbaas/members_test.go | 6 +++--- .../networking/v2/extensions/lbaas/monitors_test.go | 4 ++-- .../networking/v2/extensions/lbaas/pools_test.go | 6 +++--- .../networking/v2/extensions/lbaas/vips_test.go | 6 +++--- .../v2/extensions/lbaas_v2/l7policies_test.go | 4 ++-- .../networking/v2/extensions/lbaas_v2/lbaas_v2.go | 2 +- .../v2/extensions/lbaas_v2/listeners_test.go | 4 ++-- .../v2/extensions/lbaas_v2/loadbalancers_test.go | 6 +++--- .../networking/v2/extensions/lbaas_v2/monitors_test.go | 4 ++-- .../networking/v2/extensions/lbaas_v2/pools_test.go | 4 ++-- .../openstack/networking/v2/extensions/mtu/mtu.go | 2 +- .../openstack/networking/v2/extensions/mtu/mtu_test.go | 6 +++--- .../networkipavailabilities_test.go | 4 ++-- .../v2/extensions/portsbinding/portsbinding.go | 2 +- .../v2/extensions/portsbinding/portsbinding_test.go | 6 +++--- .../networking/v2/extensions/provider_test.go | 6 +++--- .../networking/v2/extensions/qos/policies/policies.go | 2 +- .../v2/extensions/qos/policies/policies_test.go | 4 ++-- .../networking/v2/extensions/qos/rules/rules.go | 0 .../networking/v2/extensions/qos/rules/rules_test.go | 6 +++--- .../v2/extensions/qos/ruletypes/ruletypes_test.go | 4 ++-- .../networking/v2/extensions/quotas/quotas.go | 0 .../networking/v2/extensions/quotas/quotas_test.go | 4 ++-- .../v2/extensions/rbacpolicies/rbacpolicies.go | 0 .../v2/extensions/rbacpolicies/rbacpolicies_test.go | 8 ++++---- .../networking/v2/extensions/security_test.go | 6 +++--- .../v2/extensions/subnetpools/subnetpools.go | 2 +- .../v2/extensions/subnetpools/subnetpools_test.go | 4 ++-- .../v2/extensions/trunk_details/trunks_test.go | 6 +++--- .../networking/v2/extensions/trunks/trunks.go | 2 +- .../networking/v2/extensions/trunks/trunks_test.go | 6 +++--- .../v2/extensions/vlantransparent/vlantransparent.go | 2 +- .../extensions/vlantransparent/vlantransparent_test.go | 6 +++--- .../networking/v2/extensions/vpnaas/group_test.go | 4 ++-- .../networking/v2/extensions/vpnaas/ikepolicy_test.go | 4 ++-- .../v2/extensions/vpnaas/ipsecpolicy_test.go | 4 ++-- .../networking/v2/extensions/vpnaas/service_test.go | 6 +++--- .../v2/extensions/vpnaas/siteconnection_test.go | 8 ++++---- .../networking/v2/extensions/vpnaas/vpnaas.go | 2 +- .../acceptance}/openstack/networking/v2/networking.go | 2 +- .../openstack/networking/v2/networks_test.go | 4 ++-- .../acceptance}/openstack/networking/v2/ports_test.go | 6 +++--- .../openstack/networking/v2/subnets_test.go | 6 +++--- .../openstack/objectstorage/v1/accounts_test.go | 2 +- .../openstack/objectstorage/v1/containers_test.go | 4 ++-- .../openstack/objectstorage/v1/objects_test.go | 4 ++-- .../acceptance}/openstack/objectstorage/v1/pkg.go | 0 .../openstack/objectstorage/v1/versioning_test.go | 4 ++-- .../openstack/orchestration/v1/buildinfo_test.go | 2 +- .../openstack/orchestration/v1/orchestration.go | 2 +- .../openstack/orchestration/v1/stackevents_test.go | 2 +- .../openstack/orchestration/v1/stackresources_test.go | 4 ++-- .../openstack/orchestration/v1/stacks_test.go | 4 ++-- .../openstack/orchestration/v1/stacktemplates_test.go | 4 ++-- .../openstack/orchestration/v1/testdata/samplefile | 0 {acceptance => internal/acceptance}/openstack/pkg.go | 0 .../acceptance}/openstack/placement/v1/pkg.go | 0 .../acceptance}/openstack/placement/v1/placement.go | 2 +- .../openstack/placement/v1/resourceproviders_test.go | 4 ++-- .../sharedfilesystems/v2/availabilityzones_test.go | 2 +- .../sharedfilesystems/v2/messages/messages.go | 0 .../sharedfilesystems/v2/messages/messages_test.go | 4 ++-- .../openstack/sharedfilesystems/v2/messages/pkg.go | 0 .../acceptance}/openstack/sharedfilesystems/v2/pkg.go | 0 .../openstack/sharedfilesystems/v2/replicas.go | 2 +- .../openstack/sharedfilesystems/v2/replicas_test.go | 4 ++-- .../sharedfilesystems/v2/schedulerstats_test.go | 4 ++-- .../openstack/sharedfilesystems/v2/securityservices.go | 2 +- .../sharedfilesystems/v2/securityservices_test.go | 4 ++-- .../openstack/sharedfilesystems/v2/services_test.go | 4 ++-- .../openstack/sharedfilesystems/v2/shareaccessrules.go | 2 +- .../sharedfilesystems/v2/shareaccessrules_test.go | 4 ++-- .../openstack/sharedfilesystems/v2/sharenetworks.go | 4 ++-- .../sharedfilesystems/v2/sharenetworks_test.go | 4 ++-- .../openstack/sharedfilesystems/v2/shares.go | 2 +- .../openstack/sharedfilesystems/v2/shares_test.go | 4 ++-- .../openstack/sharedfilesystems/v2/sharetransfers.go | 0 .../sharedfilesystems/v2/sharetransfers_test.go | 4 ++-- .../openstack/sharedfilesystems/v2/sharetypes.go | 2 +- .../openstack/sharedfilesystems/v2/sharetypes_test.go | 4 ++-- .../openstack/sharedfilesystems/v2/snapshots.go | 2 +- .../openstack/sharedfilesystems/v2/snapshots_test.go | 4 ++-- .../acceptance}/openstack/workflow/v2/crontrigger.go | 2 +- .../openstack/workflow/v2/crontriggers_test.go | 4 ++-- .../acceptance}/openstack/workflow/v2/execution.go | 2 +- .../openstack/workflow/v2/executions_test.go | 4 ++-- .../acceptance}/openstack/workflow/v2/workflow.go | 2 +- .../openstack/workflow/v2/workflows_test.go | 4 ++-- {acceptance => internal/acceptance}/tools/pkg.go | 0 {acceptance => internal/acceptance}/tools/tools.go | 0 .../v1/profiletypes/testing/requests_test.go | 2 +- script/acceptancetest | 2 +- 290 files changed, 498 insertions(+), 498 deletions(-) rename {acceptance => internal/acceptance}/README.md (98%) rename {acceptance => internal/acceptance}/clients/clients.go (100%) rename {acceptance => internal/acceptance}/clients/conditions.go (100%) rename {acceptance => internal/acceptance}/clients/http.go (100%) rename {acceptance => internal/acceptance}/clients/testing/conditions_test.go (96%) rename {acceptance => internal/acceptance}/openstack/baremetal/httpbasic/allocations_test.go (87%) rename {acceptance => internal/acceptance}/openstack/baremetal/httpbasic/doc.go (100%) rename {acceptance => internal/acceptance}/openstack/baremetal/httpbasic/nodes_test.go (93%) rename {acceptance => internal/acceptance}/openstack/baremetal/httpbasic/ports_test.go (92%) rename {acceptance => internal/acceptance}/openstack/baremetal/noauth/allocations_test.go (87%) rename {acceptance => internal/acceptance}/openstack/baremetal/noauth/doc.go (100%) rename {acceptance => internal/acceptance}/openstack/baremetal/noauth/nodes_test.go (93%) rename {acceptance => internal/acceptance}/openstack/baremetal/noauth/ports_test.go (91%) rename {acceptance => internal/acceptance}/openstack/baremetal/v1/allocations_test.go (94%) rename {acceptance => internal/acceptance}/openstack/baremetal/v1/baremetal.go (98%) rename {acceptance => internal/acceptance}/openstack/baremetal/v1/conductors_test.go (88%) rename {acceptance => internal/acceptance}/openstack/baremetal/v1/nodes_test.go (98%) rename {acceptance => internal/acceptance}/openstack/baremetal/v1/ports_test.go (96%) rename {acceptance => internal/acceptance}/openstack/blockstorage/apiversions_test.go (89%) rename {acceptance => internal/acceptance}/openstack/blockstorage/extensions/backups_test.go (85%) rename {acceptance => internal/acceptance}/openstack/blockstorage/extensions/extensions.go (99%) rename {acceptance => internal/acceptance}/openstack/blockstorage/extensions/limits_test.go (92%) rename {acceptance => internal/acceptance}/openstack/blockstorage/extensions/pkg.go (100%) rename {acceptance => internal/acceptance}/openstack/blockstorage/extensions/schedulerhints_test.go (91%) rename {acceptance => internal/acceptance}/openstack/blockstorage/extensions/schedulerstats_test.go (84%) rename {acceptance => internal/acceptance}/openstack/blockstorage/extensions/services_test.go (83%) rename {acceptance => internal/acceptance}/openstack/blockstorage/extensions/volumeactions_test.go (93%) rename {acceptance => internal/acceptance}/openstack/blockstorage/extensions/volumetenants_test.go (87%) rename {acceptance => internal/acceptance}/openstack/blockstorage/noauth/blockstorage.go (96%) rename {acceptance => internal/acceptance}/openstack/blockstorage/noauth/snapshots_test.go (91%) rename {acceptance => internal/acceptance}/openstack/blockstorage/noauth/volumes_test.go (90%) rename {acceptance => internal/acceptance}/openstack/blockstorage/v1/blockstorage.go (98%) rename {acceptance => internal/acceptance}/openstack/blockstorage/v1/pkg.go (100%) rename {acceptance => internal/acceptance}/openstack/blockstorage/v1/snapshots_test.go (91%) rename {acceptance => internal/acceptance}/openstack/blockstorage/v1/volumes_test.go (93%) rename {acceptance => internal/acceptance}/openstack/blockstorage/v1/volumetypes_test.go (90%) rename {acceptance => internal/acceptance}/openstack/blockstorage/v2/blockstorage.go (97%) rename {acceptance => internal/acceptance}/openstack/blockstorage/v2/pkg.go (100%) rename {acceptance => internal/acceptance}/openstack/blockstorage/v2/snapshots_test.go (89%) rename {acceptance => internal/acceptance}/openstack/blockstorage/v2/volumes_test.go (96%) rename {acceptance => internal/acceptance}/openstack/blockstorage/v3/blockstorage.go (99%) rename {acceptance => internal/acceptance}/openstack/blockstorage/v3/pkg.go (100%) rename {acceptance => internal/acceptance}/openstack/blockstorage/v3/qos_test.go (95%) rename {acceptance => internal/acceptance}/openstack/blockstorage/v3/quotaset_test.go (97%) rename {acceptance => internal/acceptance}/openstack/blockstorage/v3/snapshots_test.go (97%) rename {acceptance => internal/acceptance}/openstack/blockstorage/v3/volumeattachments.go (100%) rename {acceptance => internal/acceptance}/openstack/blockstorage/v3/volumeattachments_test.go (86%) rename {acceptance => internal/acceptance}/openstack/blockstorage/v3/volumes_test.go (96%) rename {acceptance => internal/acceptance}/openstack/blockstorage/v3/volumetypes_test.go (96%) rename {acceptance => internal/acceptance}/openstack/client_test.go (96%) rename {acceptance => internal/acceptance}/openstack/clustering/v1/actions_test.go (83%) rename {acceptance => internal/acceptance}/openstack/clustering/v1/clustering.go (98%) rename {acceptance => internal/acceptance}/openstack/clustering/v1/clusters_test.go (99%) rename {acceptance => internal/acceptance}/openstack/clustering/v1/events_test.go (82%) rename {acceptance => internal/acceptance}/openstack/clustering/v1/nodes_test.go (98%) rename {acceptance => internal/acceptance}/openstack/clustering/v1/pkg.go (100%) rename {acceptance => internal/acceptance}/openstack/clustering/v1/policies_test.go (92%) rename {acceptance => internal/acceptance}/openstack/clustering/v1/policytypes_test.go (92%) rename {acceptance => internal/acceptance}/openstack/clustering/v1/profiles_test.go (93%) rename {acceptance => internal/acceptance}/openstack/clustering/v1/profiletypes_test.go (89%) rename {acceptance => internal/acceptance}/openstack/clustering/v1/receivers_test.go (94%) rename {acceptance => internal/acceptance}/openstack/clustering/v1/webhooktrigger_test.go (96%) rename {acceptance => internal/acceptance}/openstack/common.go (100%) rename {acceptance => internal/acceptance}/openstack/compute/v2/aggregates_test.go (96%) rename {acceptance => internal/acceptance}/openstack/compute/v2/attachinterfaces_test.go (90%) rename {acceptance => internal/acceptance}/openstack/compute/v2/availabilityzones_test.go (91%) rename {acceptance => internal/acceptance}/openstack/compute/v2/bootfromvolume_test.go (97%) rename {acceptance => internal/acceptance}/openstack/compute/v2/compute.go (99%) rename {acceptance => internal/acceptance}/openstack/compute/v2/defsecrules_test.go (91%) rename {acceptance => internal/acceptance}/openstack/compute/v2/diagnostics_test.go (83%) rename {acceptance => internal/acceptance}/openstack/compute/v2/extension_test.go (88%) rename {acceptance => internal/acceptance}/openstack/compute/v2/flavors_test.go (96%) rename {acceptance => internal/acceptance}/openstack/compute/v2/floatingip_test.go (96%) rename {acceptance => internal/acceptance}/openstack/compute/v2/hypervisors_test.go (95%) rename {acceptance => internal/acceptance}/openstack/compute/v2/images_test.go (89%) rename {acceptance => internal/acceptance}/openstack/compute/v2/instance_actions_test.go (94%) rename {acceptance => internal/acceptance}/openstack/compute/v2/keypairs_test.go (94%) rename {acceptance => internal/acceptance}/openstack/compute/v2/limits_test.go (90%) rename {acceptance => internal/acceptance}/openstack/compute/v2/migrate_test.go (95%) rename {acceptance => internal/acceptance}/openstack/compute/v2/network_test.go (90%) rename {acceptance => internal/acceptance}/openstack/compute/v2/pkg.go (100%) rename {acceptance => internal/acceptance}/openstack/compute/v2/quotaset_test.go (97%) rename {acceptance => internal/acceptance}/openstack/compute/v2/remoteconsoles_test.go (81%) rename {acceptance => internal/acceptance}/openstack/compute/v2/rescueunrescue_test.go (89%) rename {acceptance => internal/acceptance}/openstack/compute/v2/secgroup_test.go (96%) rename {acceptance => internal/acceptance}/openstack/compute/v2/servergroup_test.go (95%) rename {acceptance => internal/acceptance}/openstack/compute/v2/servers_test.go (98%) rename {acceptance => internal/acceptance}/openstack/compute/v2/services_test.go (95%) rename {acceptance => internal/acceptance}/openstack/compute/v2/tenantnetworks_test.go (90%) rename {acceptance => internal/acceptance}/openstack/compute/v2/usage_test.go (93%) rename {acceptance => internal/acceptance}/openstack/compute/v2/volumeattach_test.go (81%) rename {acceptance => internal/acceptance}/openstack/container/v1/capsules.go (94%) rename {acceptance => internal/acceptance}/openstack/container/v1/capsules_test.go (97%) rename {acceptance => internal/acceptance}/openstack/container/v1/fixtures.go (100%) rename {acceptance => internal/acceptance}/openstack/containerinfra/v1/certificates_test.go (96%) rename {acceptance => internal/acceptance}/openstack/containerinfra/v1/clusters_test.go (94%) rename {acceptance => internal/acceptance}/openstack/containerinfra/v1/clustertemplates_test.go (93%) rename {acceptance => internal/acceptance}/openstack/containerinfra/v1/containerinfra.go (97%) rename {acceptance => internal/acceptance}/openstack/containerinfra/v1/nodegroups_test.go (97%) rename {acceptance => internal/acceptance}/openstack/containerinfra/v1/pkg.go (100%) rename {acceptance => internal/acceptance}/openstack/containerinfra/v1/quotas_test.go (74%) rename {acceptance => internal/acceptance}/openstack/db/v1/configurations_test.go (94%) rename {acceptance => internal/acceptance}/openstack/db/v1/databases_test.go (90%) rename {acceptance => internal/acceptance}/openstack/db/v1/db.go (97%) rename {acceptance => internal/acceptance}/openstack/db/v1/flavors_test.go (90%) rename {acceptance => internal/acceptance}/openstack/db/v1/instances_test.go (92%) rename {acceptance => internal/acceptance}/openstack/db/v1/pkg.go (100%) rename {acceptance => internal/acceptance}/openstack/db/v1/users_test.go (89%) rename {acceptance => internal/acceptance}/openstack/dns/v2/dns.go (99%) rename {acceptance => internal/acceptance}/openstack/dns/v2/recordsets_test.go (94%) rename {acceptance => internal/acceptance}/openstack/dns/v2/transfers_test.go (92%) rename {acceptance => internal/acceptance}/openstack/dns/v2/zones_test.go (88%) rename {acceptance => internal/acceptance}/openstack/identity/v2/extension_test.go (88%) rename {acceptance => internal/acceptance}/openstack/identity/v2/identity.go (98%) rename {acceptance => internal/acceptance}/openstack/identity/v2/pkg.go (100%) rename {acceptance => internal/acceptance}/openstack/identity/v2/role_test.go (92%) rename {acceptance => internal/acceptance}/openstack/identity/v2/tenant_test.go (91%) rename {acceptance => internal/acceptance}/openstack/identity/v2/token_test.go (91%) rename {acceptance => internal/acceptance}/openstack/identity/v2/user_test.go (90%) rename {acceptance => internal/acceptance}/openstack/identity/v3/applicationcredentials_test.go (98%) rename {acceptance => internal/acceptance}/openstack/identity/v3/catalog_test.go (79%) rename {acceptance => internal/acceptance}/openstack/identity/v3/credentials_test.go (97%) rename {acceptance => internal/acceptance}/openstack/identity/v3/domains_test.go (94%) rename {acceptance => internal/acceptance}/openstack/identity/v3/ec2credentials_test.go (95%) rename {acceptance => internal/acceptance}/openstack/identity/v3/endpoint_test.go (93%) rename {acceptance => internal/acceptance}/openstack/identity/v3/federation_test.go (95%) rename {acceptance => internal/acceptance}/openstack/identity/v3/groups_test.go (95%) rename {acceptance => internal/acceptance}/openstack/identity/v3/identity.go (99%) rename {acceptance => internal/acceptance}/openstack/identity/v3/limits_test.go (97%) rename {acceptance => internal/acceptance}/openstack/identity/v3/oauth1_test.go (98%) rename {acceptance => internal/acceptance}/openstack/identity/v3/osinherit_test.go (99%) rename {acceptance => internal/acceptance}/openstack/identity/v3/pkg.go (100%) rename {acceptance => internal/acceptance}/openstack/identity/v3/policies_test.go (96%) rename {acceptance => internal/acceptance}/openstack/identity/v3/projectendpoint_test.go (91%) rename {acceptance => internal/acceptance}/openstack/identity/v3/projects_test.go (98%) rename {acceptance => internal/acceptance}/openstack/identity/v3/reauth_test.go (92%) rename {acceptance => internal/acceptance}/openstack/identity/v3/regions_test.go (94%) rename {acceptance => internal/acceptance}/openstack/identity/v3/registeredlimits_test.go (96%) rename {acceptance => internal/acceptance}/openstack/identity/v3/roles_test.go (99%) rename {acceptance => internal/acceptance}/openstack/identity/v3/service_test.go (92%) rename {acceptance => internal/acceptance}/openstack/identity/v3/token_test.go (89%) rename {acceptance => internal/acceptance}/openstack/identity/v3/trusts_test.go (96%) rename {acceptance => internal/acceptance}/openstack/identity/v3/users_test.go (98%) rename {acceptance => internal/acceptance}/openstack/imageservice/v2/imagedata_test.go (82%) rename {acceptance => internal/acceptance}/openstack/imageservice/v2/imageimport_test.go (84%) rename {acceptance => internal/acceptance}/openstack/imageservice/v2/images_test.go (97%) rename {acceptance => internal/acceptance}/openstack/imageservice/v2/imageservice.go (98%) rename {acceptance => internal/acceptance}/openstack/imageservice/v2/tasks_test.go (91%) rename {acceptance => internal/acceptance}/openstack/keymanager/v1/acls_test.go (96%) rename {acceptance => internal/acceptance}/openstack/keymanager/v1/containers_test.go (97%) rename {acceptance => internal/acceptance}/openstack/keymanager/v1/keymanager.go (99%) rename {acceptance => internal/acceptance}/openstack/keymanager/v1/orders_test.go (94%) rename {acceptance => internal/acceptance}/openstack/keymanager/v1/secrets_test.go (98%) rename {acceptance => internal/acceptance}/openstack/loadbalancer/v2/amphorae_test.go (84%) rename {acceptance => internal/acceptance}/openstack/loadbalancer/v2/l7policies_test.go (85%) rename {acceptance => internal/acceptance}/openstack/loadbalancer/v2/listeners_test.go (85%) rename {acceptance => internal/acceptance}/openstack/loadbalancer/v2/loadbalancer.go (99%) rename {acceptance => internal/acceptance}/openstack/loadbalancer/v2/loadbalancers_test.go (98%) rename {acceptance => internal/acceptance}/openstack/loadbalancer/v2/monitors_test.go (84%) rename {acceptance => internal/acceptance}/openstack/loadbalancer/v2/pkg.go (100%) rename {acceptance => internal/acceptance}/openstack/loadbalancer/v2/pools_test.go (84%) rename {acceptance => internal/acceptance}/openstack/loadbalancer/v2/providers_test.go (85%) rename {acceptance => internal/acceptance}/openstack/loadbalancer/v2/quotas_test.go (94%) rename {acceptance => internal/acceptance}/openstack/messaging/v2/claims_test.go (92%) rename {acceptance => internal/acceptance}/openstack/messaging/v2/message_test.go (98%) rename {acceptance => internal/acceptance}/openstack/messaging/v2/messaging.go (98%) rename {acceptance => internal/acceptance}/openstack/messaging/v2/queue_test.go (96%) rename {acceptance => internal/acceptance}/openstack/networking/v2/apiversion_test.go (90%) rename {acceptance => internal/acceptance}/openstack/networking/v2/extension_test.go (88%) rename {acceptance => internal/acceptance}/openstack/networking/v2/extensions/agents/agents_test.go (94%) rename {acceptance => internal/acceptance}/openstack/networking/v2/extensions/agents/doc.go (100%) rename {acceptance => internal/acceptance}/openstack/networking/v2/extensions/attributestags_test.go (95%) rename {acceptance => internal/acceptance}/openstack/networking/v2/extensions/bgp/peers/bgppeers_test.go (92%) rename {acceptance => internal/acceptance}/openstack/networking/v2/extensions/bgp/peers/doc.go (100%) rename {acceptance => internal/acceptance}/openstack/networking/v2/extensions/bgp/peers/peers.go (93%) rename {acceptance => internal/acceptance}/openstack/networking/v2/extensions/bgp/speakers/bgpspeakers_test.go (92%) rename {acceptance => internal/acceptance}/openstack/networking/v2/extensions/bgp/speakers/doc.go (100%) rename {acceptance => internal/acceptance}/openstack/networking/v2/extensions/bgp/speakers/speakers.go (95%) rename {acceptance => internal/acceptance}/openstack/networking/v2/extensions/dns/dns.go (98%) rename {acceptance => internal/acceptance}/openstack/networking/v2/extensions/dns/dns_test.go (95%) rename {acceptance => internal/acceptance}/openstack/networking/v2/extensions/extensions.go (98%) rename {acceptance => internal/acceptance}/openstack/networking/v2/extensions/fwaas/firewall_test.go (95%) rename {acceptance => internal/acceptance}/openstack/networking/v2/extensions/fwaas/fwaas.go (99%) rename {acceptance => internal/acceptance}/openstack/networking/v2/extensions/fwaas/policy_test.go (91%) rename {acceptance => internal/acceptance}/openstack/networking/v2/extensions/fwaas/rule_test.go (90%) rename {acceptance => internal/acceptance}/openstack/networking/v2/extensions/fwaas_v2/fwaas_v2.go (98%) rename {acceptance => internal/acceptance}/openstack/networking/v2/extensions/fwaas_v2/groups_test.go (96%) rename {acceptance => internal/acceptance}/openstack/networking/v2/extensions/fwaas_v2/policy_test.go (94%) rename {acceptance => internal/acceptance}/openstack/networking/v2/extensions/fwaas_v2/rule_test.go (93%) rename {acceptance => internal/acceptance}/openstack/networking/v2/extensions/layer3/addressscopes_test.go (91%) rename {acceptance => internal/acceptance}/openstack/networking/v2/extensions/layer3/extraroutes_test.go (92%) rename {acceptance => internal/acceptance}/openstack/networking/v2/extensions/layer3/floatingips_test.go (96%) rename {acceptance => internal/acceptance}/openstack/networking/v2/extensions/layer3/l3_scheduling_test.go (92%) rename {acceptance => internal/acceptance}/openstack/networking/v2/extensions/layer3/layer3.go (98%) rename {acceptance => internal/acceptance}/openstack/networking/v2/extensions/layer3/portforwardings_test.go (91%) rename {acceptance => internal/acceptance}/openstack/networking/v2/extensions/layer3/routers_test.go (96%) rename {acceptance => internal/acceptance}/openstack/networking/v2/extensions/lbaas/lbaas.go (98%) rename {acceptance => internal/acceptance}/openstack/networking/v2/extensions/lbaas/members_test.go (90%) rename {acceptance => internal/acceptance}/openstack/networking/v2/extensions/lbaas/monitors_test.go (92%) rename {acceptance => internal/acceptance}/openstack/networking/v2/extensions/lbaas/pools_test.go (93%) rename {acceptance => internal/acceptance}/openstack/networking/v2/extensions/lbaas/vips_test.go (90%) rename {acceptance => internal/acceptance}/openstack/networking/v2/extensions/lbaas_v2/l7policies_test.go (86%) rename {acceptance => internal/acceptance}/openstack/networking/v2/extensions/lbaas_v2/lbaas_v2.go (99%) rename {acceptance => internal/acceptance}/openstack/networking/v2/extensions/lbaas_v2/listeners_test.go (86%) rename {acceptance => internal/acceptance}/openstack/networking/v2/extensions/lbaas_v2/loadbalancers_test.go (97%) rename {acceptance => internal/acceptance}/openstack/networking/v2/extensions/lbaas_v2/monitors_test.go (86%) rename {acceptance => internal/acceptance}/openstack/networking/v2/extensions/lbaas_v2/pools_test.go (86%) rename {acceptance => internal/acceptance}/openstack/networking/v2/extensions/mtu/mtu.go (96%) rename {acceptance => internal/acceptance}/openstack/networking/v2/extensions/mtu/mtu_test.go (94%) rename {acceptance => internal/acceptance}/openstack/networking/v2/extensions/networkipavailabilities/networkipavailabilities_test.go (87%) rename {acceptance => internal/acceptance}/openstack/networking/v2/extensions/portsbinding/portsbinding.go (96%) rename {acceptance => internal/acceptance}/openstack/networking/v2/extensions/portsbinding/portsbinding_test.go (90%) rename {acceptance => internal/acceptance}/openstack/networking/v2/extensions/provider_test.go (75%) rename {acceptance => internal/acceptance}/openstack/networking/v2/extensions/qos/policies/policies.go (95%) rename {acceptance => internal/acceptance}/openstack/networking/v2/extensions/qos/policies/policies_test.go (92%) rename {acceptance => internal/acceptance}/openstack/networking/v2/extensions/qos/rules/rules.go (100%) rename {acceptance => internal/acceptance}/openstack/networking/v2/extensions/qos/rules/rules_test.go (94%) rename {acceptance => internal/acceptance}/openstack/networking/v2/extensions/qos/ruletypes/ruletypes_test.go (89%) rename {acceptance => internal/acceptance}/openstack/networking/v2/extensions/quotas/quotas.go (100%) rename {acceptance => internal/acceptance}/openstack/networking/v2/extensions/quotas/quotas_test.go (93%) rename {acceptance => internal/acceptance}/openstack/networking/v2/extensions/rbacpolicies/rbacpolicies.go (100%) rename {acceptance => internal/acceptance}/openstack/networking/v2/extensions/rbacpolicies/rbacpolicies_test.go (87%) rename {acceptance => internal/acceptance}/openstack/networking/v2/extensions/security_test.go (90%) rename {acceptance => internal/acceptance}/openstack/networking/v2/extensions/subnetpools/subnetpools.go (96%) rename {acceptance => internal/acceptance}/openstack/networking/v2/extensions/subnetpools/subnetpools_test.go (91%) rename {acceptance => internal/acceptance}/openstack/networking/v2/extensions/trunk_details/trunks_test.go (93%) rename {acceptance => internal/acceptance}/openstack/networking/v2/extensions/trunks/trunks.go (95%) rename {acceptance => internal/acceptance}/openstack/networking/v2/extensions/trunks/trunks_test.go (97%) rename {acceptance => internal/acceptance}/openstack/networking/v2/extensions/vlantransparent/vlantransparent.go (97%) rename {acceptance => internal/acceptance}/openstack/networking/v2/extensions/vlantransparent/vlantransparent_test.go (85%) rename {acceptance => internal/acceptance}/openstack/networking/v2/extensions/vpnaas/group_test.go (91%) rename {acceptance => internal/acceptance}/openstack/networking/v2/extensions/vpnaas/ikepolicy_test.go (91%) rename {acceptance => internal/acceptance}/openstack/networking/v2/extensions/vpnaas/ipsecpolicy_test.go (90%) rename {acceptance => internal/acceptance}/openstack/networking/v2/extensions/vpnaas/service_test.go (83%) rename {acceptance => internal/acceptance}/openstack/networking/v2/extensions/vpnaas/siteconnection_test.go (89%) rename {acceptance => internal/acceptance}/openstack/networking/v2/extensions/vpnaas/vpnaas.go (99%) rename {acceptance => internal/acceptance}/openstack/networking/v2/networking.go (99%) rename {acceptance => internal/acceptance}/openstack/networking/v2/networks_test.go (97%) rename {acceptance => internal/acceptance}/openstack/networking/v2/ports_test.go (98%) rename {acceptance => internal/acceptance}/openstack/networking/v2/subnets_test.go (97%) rename {acceptance => internal/acceptance}/openstack/objectstorage/v1/accounts_test.go (95%) rename {acceptance => internal/acceptance}/openstack/objectstorage/v1/containers_test.go (98%) rename {acceptance => internal/acceptance}/openstack/objectstorage/v1/objects_test.go (98%) rename {acceptance => internal/acceptance}/openstack/objectstorage/v1/pkg.go (100%) rename {acceptance => internal/acceptance}/openstack/objectstorage/v1/versioning_test.go (97%) rename {acceptance => internal/acceptance}/openstack/orchestration/v1/buildinfo_test.go (86%) rename {acceptance => internal/acceptance}/openstack/orchestration/v1/orchestration.go (98%) rename {acceptance => internal/acceptance}/openstack/orchestration/v1/stackevents_test.go (93%) rename {acceptance => internal/acceptance}/openstack/orchestration/v1/stackresources_test.go (92%) rename {acceptance => internal/acceptance}/openstack/orchestration/v1/stacks_test.go (90%) rename {acceptance => internal/acceptance}/openstack/orchestration/v1/stacktemplates_test.go (90%) rename {acceptance => internal/acceptance}/openstack/orchestration/v1/testdata/samplefile (100%) rename {acceptance => internal/acceptance}/openstack/pkg.go (100%) rename {acceptance => internal/acceptance}/openstack/placement/v1/pkg.go (100%) rename {acceptance => internal/acceptance}/openstack/placement/v1/placement.go (97%) rename {acceptance => internal/acceptance}/openstack/placement/v1/resourceproviders_test.go (96%) rename {acceptance => internal/acceptance}/openstack/sharedfilesystems/v2/availabilityzones_test.go (91%) rename {acceptance => internal/acceptance}/openstack/sharedfilesystems/v2/messages/messages.go (100%) rename {acceptance => internal/acceptance}/openstack/sharedfilesystems/v2/messages/messages_test.go (95%) rename {acceptance => internal/acceptance}/openstack/sharedfilesystems/v2/messages/pkg.go (100%) rename {acceptance => internal/acceptance}/openstack/sharedfilesystems/v2/pkg.go (100%) rename {acceptance => internal/acceptance}/openstack/sharedfilesystems/v2/replicas.go (98%) rename {acceptance => internal/acceptance}/openstack/sharedfilesystems/v2/replicas_test.go (98%) rename {acceptance => internal/acceptance}/openstack/sharedfilesystems/v2/schedulerstats_test.go (82%) rename {acceptance => internal/acceptance}/openstack/sharedfilesystems/v2/securityservices.go (96%) rename {acceptance => internal/acceptance}/openstack/sharedfilesystems/v2/securityservices_test.go (97%) rename {acceptance => internal/acceptance}/openstack/sharedfilesystems/v2/services_test.go (83%) rename {acceptance => internal/acceptance}/openstack/sharedfilesystems/v2/shareaccessrules.go (97%) rename {acceptance => internal/acceptance}/openstack/sharedfilesystems/v2/shareaccessrules_test.go (96%) rename {acceptance => internal/acceptance}/openstack/sharedfilesystems/v2/sharenetworks.go (92%) rename {acceptance => internal/acceptance}/openstack/sharedfilesystems/v2/sharenetworks_test.go (98%) rename {acceptance => internal/acceptance}/openstack/sharedfilesystems/v2/shares.go (98%) rename {acceptance => internal/acceptance}/openstack/sharedfilesystems/v2/shares_test.go (99%) rename {acceptance => internal/acceptance}/openstack/sharedfilesystems/v2/sharetransfers.go (100%) rename {acceptance => internal/acceptance}/openstack/sharedfilesystems/v2/sharetransfers_test.go (93%) rename {acceptance => internal/acceptance}/openstack/sharedfilesystems/v2/sharetypes.go (95%) rename {acceptance => internal/acceptance}/openstack/sharedfilesystems/v2/sharetypes_test.go (97%) rename {acceptance => internal/acceptance}/openstack/sharedfilesystems/v2/snapshots.go (97%) rename {acceptance => internal/acceptance}/openstack/sharedfilesystems/v2/snapshots_test.go (97%) rename {acceptance => internal/acceptance}/openstack/workflow/v2/crontrigger.go (97%) rename {acceptance => internal/acceptance}/openstack/workflow/v2/crontriggers_test.go (91%) rename {acceptance => internal/acceptance}/openstack/workflow/v2/execution.go (97%) rename {acceptance => internal/acceptance}/openstack/workflow/v2/executions_test.go (90%) rename {acceptance => internal/acceptance}/openstack/workflow/v2/workflow.go (97%) rename {acceptance => internal/acceptance}/openstack/workflow/v2/workflows_test.go (89%) rename {acceptance => internal/acceptance}/tools/pkg.go (100%) rename {acceptance => internal/acceptance}/tools/tools.go (100%) diff --git a/.github/workflows/functional-basic.yaml b/.github/workflows/functional-basic.yaml index 17225e1e5f..b836cfb711 100644 --- a/.github/workflows/functional-basic.yaml +++ b/.github/workflows/functional-basic.yaml @@ -51,7 +51,7 @@ jobs: run: ./script/acceptancetest env: DEVSTACK_PATH: ${{ github.workspace }}/devstack - ACCEPTANCE_TESTS_FILTER: '^acceptance/openstack$' + ACCEPTANCE_TESTS_FILTER: '^internal/acceptance/openstack$' OS_BRANCH: ${{ matrix.openstack_version }} - name: Generate logs on failure run: ./script/collectlogs diff --git a/acceptance/README.md b/internal/acceptance/README.md similarity index 98% rename from acceptance/README.md rename to internal/acceptance/README.md index 834cdc12ef..cbbbac7f39 100644 --- a/acceptance/README.md +++ b/internal/acceptance/README.md @@ -100,7 +100,7 @@ Alternatively, add the following to your `.bashrc`: gophercloudtest() { if [[ -n $1 ]] && [[ -n $2 ]]; then pushd $GOPATH/src/github.com/gophercloud/gophercloud - go test -v -tags "fixtures acceptance" -run "$1" github.com/gophercloud/gophercloud/acceptance/openstack/$2 | tee ~/gophercloud.log + go test -v -tags "fixtures acceptance" -run "$1" github.com/gophercloud/gophercloud/internal/acceptance/openstack/$2 | tee ~/gophercloud.log popd fi } diff --git a/acceptance/clients/clients.go b/internal/acceptance/clients/clients.go similarity index 100% rename from acceptance/clients/clients.go rename to internal/acceptance/clients/clients.go diff --git a/acceptance/clients/conditions.go b/internal/acceptance/clients/conditions.go similarity index 100% rename from acceptance/clients/conditions.go rename to internal/acceptance/clients/conditions.go diff --git a/acceptance/clients/http.go b/internal/acceptance/clients/http.go similarity index 100% rename from acceptance/clients/http.go rename to internal/acceptance/clients/http.go diff --git a/acceptance/clients/testing/conditions_test.go b/internal/acceptance/clients/testing/conditions_test.go similarity index 96% rename from acceptance/clients/testing/conditions_test.go rename to internal/acceptance/clients/testing/conditions_test.go index a9825ea293..3fbe11260e 100644 --- a/acceptance/clients/testing/conditions_test.go +++ b/internal/acceptance/clients/testing/conditions_test.go @@ -5,7 +5,7 @@ import ( "os" "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" ) func TestIsCurrentAbove(t *testing.T) { diff --git a/acceptance/openstack/baremetal/httpbasic/allocations_test.go b/internal/acceptance/openstack/baremetal/httpbasic/allocations_test.go similarity index 87% rename from acceptance/openstack/baremetal/httpbasic/allocations_test.go rename to internal/acceptance/openstack/baremetal/httpbasic/allocations_test.go index afe44a0cf2..89acfb6a03 100644 --- a/acceptance/openstack/baremetal/httpbasic/allocations_test.go +++ b/internal/acceptance/openstack/baremetal/httpbasic/allocations_test.go @@ -6,8 +6,8 @@ package httpbasic import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - v1 "github.com/gophercloud/gophercloud/acceptance/openstack/baremetal/v1" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + v1 "github.com/gophercloud/gophercloud/internal/acceptance/openstack/baremetal/v1" "github.com/gophercloud/gophercloud/openstack/baremetal/v1/allocations" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" diff --git a/acceptance/openstack/baremetal/httpbasic/doc.go b/internal/acceptance/openstack/baremetal/httpbasic/doc.go similarity index 100% rename from acceptance/openstack/baremetal/httpbasic/doc.go rename to internal/acceptance/openstack/baremetal/httpbasic/doc.go diff --git a/acceptance/openstack/baremetal/httpbasic/nodes_test.go b/internal/acceptance/openstack/baremetal/httpbasic/nodes_test.go similarity index 93% rename from acceptance/openstack/baremetal/httpbasic/nodes_test.go rename to internal/acceptance/openstack/baremetal/httpbasic/nodes_test.go index 79995912e3..993a3ca227 100644 --- a/acceptance/openstack/baremetal/httpbasic/nodes_test.go +++ b/internal/acceptance/openstack/baremetal/httpbasic/nodes_test.go @@ -3,8 +3,8 @@ package httpbasic import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - v1 "github.com/gophercloud/gophercloud/acceptance/openstack/baremetal/v1" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + v1 "github.com/gophercloud/gophercloud/internal/acceptance/openstack/baremetal/v1" "github.com/gophercloud/gophercloud/openstack/baremetal/v1/nodes" "github.com/gophercloud/gophercloud/pagination" diff --git a/acceptance/openstack/baremetal/httpbasic/ports_test.go b/internal/acceptance/openstack/baremetal/httpbasic/ports_test.go similarity index 92% rename from acceptance/openstack/baremetal/httpbasic/ports_test.go rename to internal/acceptance/openstack/baremetal/httpbasic/ports_test.go index ebed5b6785..f69ba2744a 100644 --- a/acceptance/openstack/baremetal/httpbasic/ports_test.go +++ b/internal/acceptance/openstack/baremetal/httpbasic/ports_test.go @@ -6,8 +6,8 @@ package httpbasic import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - v1 "github.com/gophercloud/gophercloud/acceptance/openstack/baremetal/v1" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + v1 "github.com/gophercloud/gophercloud/internal/acceptance/openstack/baremetal/v1" "github.com/gophercloud/gophercloud/openstack/baremetal/v1/ports" "github.com/gophercloud/gophercloud/pagination" diff --git a/acceptance/openstack/baremetal/noauth/allocations_test.go b/internal/acceptance/openstack/baremetal/noauth/allocations_test.go similarity index 87% rename from acceptance/openstack/baremetal/noauth/allocations_test.go rename to internal/acceptance/openstack/baremetal/noauth/allocations_test.go index 825fd4d93e..fe02a1fdb6 100644 --- a/acceptance/openstack/baremetal/noauth/allocations_test.go +++ b/internal/acceptance/openstack/baremetal/noauth/allocations_test.go @@ -6,8 +6,8 @@ package noauth import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - v1 "github.com/gophercloud/gophercloud/acceptance/openstack/baremetal/v1" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + v1 "github.com/gophercloud/gophercloud/internal/acceptance/openstack/baremetal/v1" "github.com/gophercloud/gophercloud/openstack/baremetal/v1/allocations" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" diff --git a/acceptance/openstack/baremetal/noauth/doc.go b/internal/acceptance/openstack/baremetal/noauth/doc.go similarity index 100% rename from acceptance/openstack/baremetal/noauth/doc.go rename to internal/acceptance/openstack/baremetal/noauth/doc.go diff --git a/acceptance/openstack/baremetal/noauth/nodes_test.go b/internal/acceptance/openstack/baremetal/noauth/nodes_test.go similarity index 93% rename from acceptance/openstack/baremetal/noauth/nodes_test.go rename to internal/acceptance/openstack/baremetal/noauth/nodes_test.go index 09969992a3..3d2f36c6f7 100644 --- a/acceptance/openstack/baremetal/noauth/nodes_test.go +++ b/internal/acceptance/openstack/baremetal/noauth/nodes_test.go @@ -3,8 +3,8 @@ package noauth import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - v1 "github.com/gophercloud/gophercloud/acceptance/openstack/baremetal/v1" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + v1 "github.com/gophercloud/gophercloud/internal/acceptance/openstack/baremetal/v1" "github.com/gophercloud/gophercloud/openstack/baremetal/v1/nodes" "github.com/gophercloud/gophercloud/pagination" diff --git a/acceptance/openstack/baremetal/noauth/ports_test.go b/internal/acceptance/openstack/baremetal/noauth/ports_test.go similarity index 91% rename from acceptance/openstack/baremetal/noauth/ports_test.go rename to internal/acceptance/openstack/baremetal/noauth/ports_test.go index a45b26d462..bb59ec6ad4 100644 --- a/acceptance/openstack/baremetal/noauth/ports_test.go +++ b/internal/acceptance/openstack/baremetal/noauth/ports_test.go @@ -6,8 +6,8 @@ package noauth import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - v1 "github.com/gophercloud/gophercloud/acceptance/openstack/baremetal/v1" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + v1 "github.com/gophercloud/gophercloud/internal/acceptance/openstack/baremetal/v1" "github.com/gophercloud/gophercloud/openstack/baremetal/v1/ports" "github.com/gophercloud/gophercloud/pagination" diff --git a/acceptance/openstack/baremetal/v1/allocations_test.go b/internal/acceptance/openstack/baremetal/v1/allocations_test.go similarity index 94% rename from acceptance/openstack/baremetal/v1/allocations_test.go rename to internal/acceptance/openstack/baremetal/v1/allocations_test.go index 7c6bfac52f..c902f4219d 100644 --- a/acceptance/openstack/baremetal/v1/allocations_test.go +++ b/internal/acceptance/openstack/baremetal/v1/allocations_test.go @@ -6,7 +6,7 @@ package v1 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" "github.com/gophercloud/gophercloud/openstack/baremetal/v1/allocations" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" diff --git a/acceptance/openstack/baremetal/v1/baremetal.go b/internal/acceptance/openstack/baremetal/v1/baremetal.go similarity index 98% rename from acceptance/openstack/baremetal/v1/baremetal.go rename to internal/acceptance/openstack/baremetal/v1/baremetal.go index 1278de533a..5849957fb8 100644 --- a/acceptance/openstack/baremetal/v1/baremetal.go +++ b/internal/acceptance/openstack/baremetal/v1/baremetal.go @@ -4,7 +4,7 @@ import ( "testing" "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/baremetal/v1/allocations" "github.com/gophercloud/gophercloud/openstack/baremetal/v1/nodes" "github.com/gophercloud/gophercloud/openstack/baremetal/v1/ports" diff --git a/acceptance/openstack/baremetal/v1/conductors_test.go b/internal/acceptance/openstack/baremetal/v1/conductors_test.go similarity index 88% rename from acceptance/openstack/baremetal/v1/conductors_test.go rename to internal/acceptance/openstack/baremetal/v1/conductors_test.go index 5324a7aa1c..4dc294c4b6 100644 --- a/acceptance/openstack/baremetal/v1/conductors_test.go +++ b/internal/acceptance/openstack/baremetal/v1/conductors_test.go @@ -6,8 +6,8 @@ package v1 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/baremetal/v1/conductors" "github.com/gophercloud/gophercloud/pagination" diff --git a/acceptance/openstack/baremetal/v1/nodes_test.go b/internal/acceptance/openstack/baremetal/v1/nodes_test.go similarity index 98% rename from acceptance/openstack/baremetal/v1/nodes_test.go rename to internal/acceptance/openstack/baremetal/v1/nodes_test.go index 2c0af7c499..af79bfd5d7 100644 --- a/acceptance/openstack/baremetal/v1/nodes_test.go +++ b/internal/acceptance/openstack/baremetal/v1/nodes_test.go @@ -6,7 +6,7 @@ package v1 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" "github.com/gophercloud/gophercloud/openstack/baremetal/v1/nodes" "github.com/gophercloud/gophercloud/pagination" diff --git a/acceptance/openstack/baremetal/v1/ports_test.go b/internal/acceptance/openstack/baremetal/v1/ports_test.go similarity index 96% rename from acceptance/openstack/baremetal/v1/ports_test.go rename to internal/acceptance/openstack/baremetal/v1/ports_test.go index e263402c29..069a31028d 100644 --- a/acceptance/openstack/baremetal/v1/ports_test.go +++ b/internal/acceptance/openstack/baremetal/v1/ports_test.go @@ -6,7 +6,7 @@ package v1 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" "github.com/gophercloud/gophercloud/openstack/baremetal/v1/ports" "github.com/gophercloud/gophercloud/pagination" diff --git a/acceptance/openstack/blockstorage/apiversions_test.go b/internal/acceptance/openstack/blockstorage/apiversions_test.go similarity index 89% rename from acceptance/openstack/blockstorage/apiversions_test.go rename to internal/acceptance/openstack/blockstorage/apiversions_test.go index 77ccda0f31..b9ff57b83f 100644 --- a/acceptance/openstack/blockstorage/apiversions_test.go +++ b/internal/acceptance/openstack/blockstorage/apiversions_test.go @@ -6,8 +6,8 @@ package blockstorage import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/blockstorage/apiversions" ) diff --git a/acceptance/openstack/blockstorage/extensions/backups_test.go b/internal/acceptance/openstack/blockstorage/extensions/backups_test.go similarity index 85% rename from acceptance/openstack/blockstorage/extensions/backups_test.go rename to internal/acceptance/openstack/blockstorage/extensions/backups_test.go index 67f86c8eb6..eea44b511d 100644 --- a/acceptance/openstack/blockstorage/extensions/backups_test.go +++ b/internal/acceptance/openstack/blockstorage/extensions/backups_test.go @@ -6,10 +6,10 @@ package extensions import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/backups" - blockstorage "github.com/gophercloud/gophercloud/acceptance/openstack/blockstorage/v3" + blockstorage "github.com/gophercloud/gophercloud/internal/acceptance/openstack/blockstorage/v3" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/blockstorage/extensions/extensions.go b/internal/acceptance/openstack/blockstorage/extensions/extensions.go similarity index 99% rename from acceptance/openstack/blockstorage/extensions/extensions.go rename to internal/acceptance/openstack/blockstorage/extensions/extensions.go index dfa357fdd3..dca1062035 100644 --- a/acceptance/openstack/blockstorage/extensions/extensions.go +++ b/internal/acceptance/openstack/blockstorage/extensions/extensions.go @@ -9,7 +9,7 @@ import ( "testing" "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/backups" "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/volumeactions" "github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes" diff --git a/acceptance/openstack/blockstorage/extensions/limits_test.go b/internal/acceptance/openstack/blockstorage/extensions/limits_test.go similarity index 92% rename from acceptance/openstack/blockstorage/extensions/limits_test.go rename to internal/acceptance/openstack/blockstorage/extensions/limits_test.go index c1b3994f9c..4ea356f657 100644 --- a/acceptance/openstack/blockstorage/extensions/limits_test.go +++ b/internal/acceptance/openstack/blockstorage/extensions/limits_test.go @@ -3,8 +3,8 @@ package extensions import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/limits" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/blockstorage/extensions/pkg.go b/internal/acceptance/openstack/blockstorage/extensions/pkg.go similarity index 100% rename from acceptance/openstack/blockstorage/extensions/pkg.go rename to internal/acceptance/openstack/blockstorage/extensions/pkg.go diff --git a/acceptance/openstack/blockstorage/extensions/schedulerhints_test.go b/internal/acceptance/openstack/blockstorage/extensions/schedulerhints_test.go similarity index 91% rename from acceptance/openstack/blockstorage/extensions/schedulerhints_test.go rename to internal/acceptance/openstack/blockstorage/extensions/schedulerhints_test.go index 8ff9fbb6a1..9864428324 100644 --- a/acceptance/openstack/blockstorage/extensions/schedulerhints_test.go +++ b/internal/acceptance/openstack/blockstorage/extensions/schedulerhints_test.go @@ -6,8 +6,8 @@ package extensions import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/schedulerhints" "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes" th "github.com/gophercloud/gophercloud/testhelper" diff --git a/acceptance/openstack/blockstorage/extensions/schedulerstats_test.go b/internal/acceptance/openstack/blockstorage/extensions/schedulerstats_test.go similarity index 84% rename from acceptance/openstack/blockstorage/extensions/schedulerstats_test.go rename to internal/acceptance/openstack/blockstorage/extensions/schedulerstats_test.go index 7b5f609b1c..f30c4c6120 100644 --- a/acceptance/openstack/blockstorage/extensions/schedulerstats_test.go +++ b/internal/acceptance/openstack/blockstorage/extensions/schedulerstats_test.go @@ -6,8 +6,8 @@ package extensions import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/schedulerstats" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/blockstorage/extensions/services_test.go b/internal/acceptance/openstack/blockstorage/extensions/services_test.go similarity index 83% rename from acceptance/openstack/blockstorage/extensions/services_test.go rename to internal/acceptance/openstack/blockstorage/extensions/services_test.go index 863d38aaec..9f32132a54 100644 --- a/acceptance/openstack/blockstorage/extensions/services_test.go +++ b/internal/acceptance/openstack/blockstorage/extensions/services_test.go @@ -6,8 +6,8 @@ package extensions import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/services" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/blockstorage/extensions/volumeactions_test.go b/internal/acceptance/openstack/blockstorage/extensions/volumeactions_test.go similarity index 93% rename from acceptance/openstack/blockstorage/extensions/volumeactions_test.go rename to internal/acceptance/openstack/blockstorage/extensions/volumeactions_test.go index 98541419df..673e968297 100644 --- a/acceptance/openstack/blockstorage/extensions/volumeactions_test.go +++ b/internal/acceptance/openstack/blockstorage/extensions/volumeactions_test.go @@ -6,11 +6,11 @@ package extensions import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - blockstorage "github.com/gophercloud/gophercloud/acceptance/openstack/blockstorage/v2" - blockstorageV3 "github.com/gophercloud/gophercloud/acceptance/openstack/blockstorage/v3" - compute "github.com/gophercloud/gophercloud/acceptance/openstack/compute/v2" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + blockstorage "github.com/gophercloud/gophercloud/internal/acceptance/openstack/blockstorage/v2" + blockstorageV3 "github.com/gophercloud/gophercloud/internal/acceptance/openstack/blockstorage/v3" + compute "github.com/gophercloud/gophercloud/internal/acceptance/openstack/compute/v2" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/blockstorage/extensions/volumetenants_test.go b/internal/acceptance/openstack/blockstorage/extensions/volumetenants_test.go similarity index 87% rename from acceptance/openstack/blockstorage/extensions/volumetenants_test.go rename to internal/acceptance/openstack/blockstorage/extensions/volumetenants_test.go index e684a79423..2571588537 100644 --- a/acceptance/openstack/blockstorage/extensions/volumetenants_test.go +++ b/internal/acceptance/openstack/blockstorage/extensions/volumetenants_test.go @@ -6,8 +6,8 @@ package extensions import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - blockstorage "github.com/gophercloud/gophercloud/acceptance/openstack/blockstorage/v3" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + blockstorage "github.com/gophercloud/gophercloud/internal/acceptance/openstack/blockstorage/v3" "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/volumetenants" "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes" th "github.com/gophercloud/gophercloud/testhelper" diff --git a/acceptance/openstack/blockstorage/noauth/blockstorage.go b/internal/acceptance/openstack/blockstorage/noauth/blockstorage.go similarity index 96% rename from acceptance/openstack/blockstorage/noauth/blockstorage.go rename to internal/acceptance/openstack/blockstorage/noauth/blockstorage.go index d057c2e604..6f5580714f 100644 --- a/acceptance/openstack/blockstorage/noauth/blockstorage.go +++ b/internal/acceptance/openstack/blockstorage/noauth/blockstorage.go @@ -7,8 +7,8 @@ import ( "testing" "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/snapshots" "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes" ) diff --git a/acceptance/openstack/blockstorage/noauth/snapshots_test.go b/internal/acceptance/openstack/blockstorage/noauth/snapshots_test.go similarity index 91% rename from acceptance/openstack/blockstorage/noauth/snapshots_test.go rename to internal/acceptance/openstack/blockstorage/noauth/snapshots_test.go index 2c0cc63dec..a12d1fa66b 100644 --- a/acceptance/openstack/blockstorage/noauth/snapshots_test.go +++ b/internal/acceptance/openstack/blockstorage/noauth/snapshots_test.go @@ -6,8 +6,8 @@ package noauth import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/snapshots" ) diff --git a/acceptance/openstack/blockstorage/noauth/volumes_test.go b/internal/acceptance/openstack/blockstorage/noauth/volumes_test.go similarity index 90% rename from acceptance/openstack/blockstorage/noauth/volumes_test.go rename to internal/acceptance/openstack/blockstorage/noauth/volumes_test.go index 5f80d07cfb..5a66210a33 100644 --- a/acceptance/openstack/blockstorage/noauth/volumes_test.go +++ b/internal/acceptance/openstack/blockstorage/noauth/volumes_test.go @@ -6,8 +6,8 @@ package noauth import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes" ) diff --git a/acceptance/openstack/blockstorage/v1/blockstorage.go b/internal/acceptance/openstack/blockstorage/v1/blockstorage.go similarity index 98% rename from acceptance/openstack/blockstorage/v1/blockstorage.go rename to internal/acceptance/openstack/blockstorage/v1/blockstorage.go index 670eaad50d..e380f80caf 100644 --- a/acceptance/openstack/blockstorage/v1/blockstorage.go +++ b/internal/acceptance/openstack/blockstorage/v1/blockstorage.go @@ -7,7 +7,7 @@ import ( "testing" "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/blockstorage/v1/snapshots" "github.com/gophercloud/gophercloud/openstack/blockstorage/v1/volumes" "github.com/gophercloud/gophercloud/openstack/blockstorage/v1/volumetypes" diff --git a/acceptance/openstack/blockstorage/v1/pkg.go b/internal/acceptance/openstack/blockstorage/v1/pkg.go similarity index 100% rename from acceptance/openstack/blockstorage/v1/pkg.go rename to internal/acceptance/openstack/blockstorage/v1/pkg.go diff --git a/acceptance/openstack/blockstorage/v1/snapshots_test.go b/internal/acceptance/openstack/blockstorage/v1/snapshots_test.go similarity index 91% rename from acceptance/openstack/blockstorage/v1/snapshots_test.go rename to internal/acceptance/openstack/blockstorage/v1/snapshots_test.go index 9ff2c192a4..8b02b35486 100644 --- a/acceptance/openstack/blockstorage/v1/snapshots_test.go +++ b/internal/acceptance/openstack/blockstorage/v1/snapshots_test.go @@ -6,8 +6,8 @@ package v1 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/blockstorage/v1/snapshots" ) diff --git a/acceptance/openstack/blockstorage/v1/volumes_test.go b/internal/acceptance/openstack/blockstorage/v1/volumes_test.go similarity index 93% rename from acceptance/openstack/blockstorage/v1/volumes_test.go rename to internal/acceptance/openstack/blockstorage/v1/volumes_test.go index c5aba5d539..d22701955c 100644 --- a/acceptance/openstack/blockstorage/v1/volumes_test.go +++ b/internal/acceptance/openstack/blockstorage/v1/volumes_test.go @@ -6,8 +6,8 @@ package v1 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/blockstorage/v1/volumes" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/blockstorage/v1/volumetypes_test.go b/internal/acceptance/openstack/blockstorage/v1/volumetypes_test.go similarity index 90% rename from acceptance/openstack/blockstorage/v1/volumetypes_test.go rename to internal/acceptance/openstack/blockstorage/v1/volumetypes_test.go index c1e76a740e..f21fe5eae1 100644 --- a/acceptance/openstack/blockstorage/v1/volumetypes_test.go +++ b/internal/acceptance/openstack/blockstorage/v1/volumetypes_test.go @@ -6,8 +6,8 @@ package v1 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/blockstorage/v1/volumetypes" ) diff --git a/acceptance/openstack/blockstorage/v2/blockstorage.go b/internal/acceptance/openstack/blockstorage/v2/blockstorage.go similarity index 97% rename from acceptance/openstack/blockstorage/v2/blockstorage.go rename to internal/acceptance/openstack/blockstorage/v2/blockstorage.go index 6170d555ba..e3027abce9 100644 --- a/acceptance/openstack/blockstorage/v2/blockstorage.go +++ b/internal/acceptance/openstack/blockstorage/v2/blockstorage.go @@ -7,8 +7,8 @@ import ( "testing" "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/blockstorage/v2/snapshots" "github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes" th "github.com/gophercloud/gophercloud/testhelper" diff --git a/acceptance/openstack/blockstorage/v2/pkg.go b/internal/acceptance/openstack/blockstorage/v2/pkg.go similarity index 100% rename from acceptance/openstack/blockstorage/v2/pkg.go rename to internal/acceptance/openstack/blockstorage/v2/pkg.go diff --git a/acceptance/openstack/blockstorage/v2/snapshots_test.go b/internal/acceptance/openstack/blockstorage/v2/snapshots_test.go similarity index 89% rename from acceptance/openstack/blockstorage/v2/snapshots_test.go rename to internal/acceptance/openstack/blockstorage/v2/snapshots_test.go index 22a8a6bd8a..ec8a2775d8 100644 --- a/acceptance/openstack/blockstorage/v2/snapshots_test.go +++ b/internal/acceptance/openstack/blockstorage/v2/snapshots_test.go @@ -6,8 +6,8 @@ package v2 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/blockstorage/v2/snapshots" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/blockstorage/v2/volumes_test.go b/internal/acceptance/openstack/blockstorage/v2/volumes_test.go similarity index 96% rename from acceptance/openstack/blockstorage/v2/volumes_test.go rename to internal/acceptance/openstack/blockstorage/v2/volumes_test.go index f2f839cc98..3ca33c1b6c 100644 --- a/acceptance/openstack/blockstorage/v2/volumes_test.go +++ b/internal/acceptance/openstack/blockstorage/v2/volumes_test.go @@ -6,8 +6,8 @@ package v2 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/volumeactions" "github.com/gophercloud/gophercloud/openstack/blockstorage/v2/snapshots" "github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes" diff --git a/acceptance/openstack/blockstorage/v3/blockstorage.go b/internal/acceptance/openstack/blockstorage/v3/blockstorage.go similarity index 99% rename from acceptance/openstack/blockstorage/v3/blockstorage.go rename to internal/acceptance/openstack/blockstorage/v3/blockstorage.go index ad6518b7de..6a4408a543 100644 --- a/acceptance/openstack/blockstorage/v3/blockstorage.go +++ b/internal/acceptance/openstack/blockstorage/v3/blockstorage.go @@ -7,7 +7,7 @@ import ( "testing" "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/qos" "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/snapshots" "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes" diff --git a/acceptance/openstack/blockstorage/v3/pkg.go b/internal/acceptance/openstack/blockstorage/v3/pkg.go similarity index 100% rename from acceptance/openstack/blockstorage/v3/pkg.go rename to internal/acceptance/openstack/blockstorage/v3/pkg.go diff --git a/acceptance/openstack/blockstorage/v3/qos_test.go b/internal/acceptance/openstack/blockstorage/v3/qos_test.go similarity index 95% rename from acceptance/openstack/blockstorage/v3/qos_test.go rename to internal/acceptance/openstack/blockstorage/v3/qos_test.go index 82c52a384a..85aa5b1908 100644 --- a/acceptance/openstack/blockstorage/v3/qos_test.go +++ b/internal/acceptance/openstack/blockstorage/v3/qos_test.go @@ -3,8 +3,8 @@ package v3 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/qos" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" diff --git a/acceptance/openstack/blockstorage/v3/quotaset_test.go b/internal/acceptance/openstack/blockstorage/v3/quotaset_test.go similarity index 97% rename from acceptance/openstack/blockstorage/v3/quotaset_test.go rename to internal/acceptance/openstack/blockstorage/v3/quotaset_test.go index 5a9d702954..1106f160da 100644 --- a/acceptance/openstack/blockstorage/v3/quotaset_test.go +++ b/internal/acceptance/openstack/blockstorage/v3/quotaset_test.go @@ -8,8 +8,8 @@ import ( "testing" "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/quotasets" "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumetypes" th "github.com/gophercloud/gophercloud/testhelper" diff --git a/acceptance/openstack/blockstorage/v3/snapshots_test.go b/internal/acceptance/openstack/blockstorage/v3/snapshots_test.go similarity index 97% rename from acceptance/openstack/blockstorage/v3/snapshots_test.go rename to internal/acceptance/openstack/blockstorage/v3/snapshots_test.go index 3c3d08ef4c..51cdd1f461 100644 --- a/acceptance/openstack/blockstorage/v3/snapshots_test.go +++ b/internal/acceptance/openstack/blockstorage/v3/snapshots_test.go @@ -8,8 +8,8 @@ import ( "testing" "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/snapshots" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" diff --git a/acceptance/openstack/blockstorage/v3/volumeattachments.go b/internal/acceptance/openstack/blockstorage/v3/volumeattachments.go similarity index 100% rename from acceptance/openstack/blockstorage/v3/volumeattachments.go rename to internal/acceptance/openstack/blockstorage/v3/volumeattachments.go diff --git a/acceptance/openstack/blockstorage/v3/volumeattachments_test.go b/internal/acceptance/openstack/blockstorage/v3/volumeattachments_test.go similarity index 86% rename from acceptance/openstack/blockstorage/v3/volumeattachments_test.go rename to internal/acceptance/openstack/blockstorage/v3/volumeattachments_test.go index a8cd3b005e..5d2b939dc0 100644 --- a/acceptance/openstack/blockstorage/v3/volumeattachments_test.go +++ b/internal/acceptance/openstack/blockstorage/v3/volumeattachments_test.go @@ -6,8 +6,8 @@ package v3 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - compute "github.com/gophercloud/gophercloud/acceptance/openstack/compute/v2" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + compute "github.com/gophercloud/gophercloud/internal/acceptance/openstack/compute/v2" "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/blockstorage/v3/volumes_test.go b/internal/acceptance/openstack/blockstorage/v3/volumes_test.go similarity index 96% rename from acceptance/openstack/blockstorage/v3/volumes_test.go rename to internal/acceptance/openstack/blockstorage/v3/volumes_test.go index b86f70863a..3bf405e3d5 100644 --- a/acceptance/openstack/blockstorage/v3/volumes_test.go +++ b/internal/acceptance/openstack/blockstorage/v3/volumes_test.go @@ -6,8 +6,8 @@ package v3 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/snapshots" "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes" "github.com/gophercloud/gophercloud/pagination" diff --git a/acceptance/openstack/blockstorage/v3/volumetypes_test.go b/internal/acceptance/openstack/blockstorage/v3/volumetypes_test.go similarity index 96% rename from acceptance/openstack/blockstorage/v3/volumetypes_test.go rename to internal/acceptance/openstack/blockstorage/v3/volumetypes_test.go index 6ac7086c3a..84b5c0fd39 100644 --- a/acceptance/openstack/blockstorage/v3/volumetypes_test.go +++ b/internal/acceptance/openstack/blockstorage/v3/volumetypes_test.go @@ -6,9 +6,9 @@ package v3 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - identity "github.com/gophercloud/gophercloud/acceptance/openstack/identity/v3" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + identity "github.com/gophercloud/gophercloud/internal/acceptance/openstack/identity/v3" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumetypes" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/client_test.go b/internal/acceptance/openstack/client_test.go similarity index 96% rename from acceptance/openstack/client_test.go rename to internal/acceptance/openstack/client_test.go index d497c969a9..366dd8b664 100644 --- a/acceptance/openstack/client_test.go +++ b/internal/acceptance/openstack/client_test.go @@ -9,8 +9,8 @@ import ( "time" "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack" "github.com/gophercloud/gophercloud/openstack/identity/v3/credentials" "github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/ec2tokens" diff --git a/acceptance/openstack/clustering/v1/actions_test.go b/internal/acceptance/openstack/clustering/v1/actions_test.go similarity index 83% rename from acceptance/openstack/clustering/v1/actions_test.go rename to internal/acceptance/openstack/clustering/v1/actions_test.go index f8a7843eb1..9ec67640f4 100644 --- a/acceptance/openstack/clustering/v1/actions_test.go +++ b/internal/acceptance/openstack/clustering/v1/actions_test.go @@ -6,8 +6,8 @@ package v1 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/clustering/v1/actions" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/clustering/v1/clustering.go b/internal/acceptance/openstack/clustering/v1/clustering.go similarity index 98% rename from acceptance/openstack/clustering/v1/clustering.go rename to internal/acceptance/openstack/clustering/v1/clustering.go index 6c7a406796..d256bf2790 100644 --- a/acceptance/openstack/clustering/v1/clustering.go +++ b/internal/acceptance/openstack/clustering/v1/clustering.go @@ -7,8 +7,8 @@ import ( "testing" "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/clustering/v1/actions" "github.com/gophercloud/gophercloud/openstack/clustering/v1/clusters" "github.com/gophercloud/gophercloud/openstack/clustering/v1/nodes" diff --git a/acceptance/openstack/clustering/v1/clusters_test.go b/internal/acceptance/openstack/clustering/v1/clusters_test.go similarity index 99% rename from acceptance/openstack/clustering/v1/clusters_test.go rename to internal/acceptance/openstack/clustering/v1/clusters_test.go index f5e72704c2..9d37267ae2 100644 --- a/acceptance/openstack/clustering/v1/clusters_test.go +++ b/internal/acceptance/openstack/clustering/v1/clusters_test.go @@ -8,8 +8,8 @@ import ( "strings" "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/clustering/v1/actions" "github.com/gophercloud/gophercloud/openstack/clustering/v1/clusters" th "github.com/gophercloud/gophercloud/testhelper" diff --git a/acceptance/openstack/clustering/v1/events_test.go b/internal/acceptance/openstack/clustering/v1/events_test.go similarity index 82% rename from acceptance/openstack/clustering/v1/events_test.go rename to internal/acceptance/openstack/clustering/v1/events_test.go index 6e8b50a827..cb849ad880 100644 --- a/acceptance/openstack/clustering/v1/events_test.go +++ b/internal/acceptance/openstack/clustering/v1/events_test.go @@ -6,8 +6,8 @@ package v1 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/clustering/v1/events" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/clustering/v1/nodes_test.go b/internal/acceptance/openstack/clustering/v1/nodes_test.go similarity index 98% rename from acceptance/openstack/clustering/v1/nodes_test.go rename to internal/acceptance/openstack/clustering/v1/nodes_test.go index 50d178672d..f35c3d4a46 100644 --- a/acceptance/openstack/clustering/v1/nodes_test.go +++ b/internal/acceptance/openstack/clustering/v1/nodes_test.go @@ -6,8 +6,8 @@ package v1 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/clustering/v1/nodes" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/clustering/v1/pkg.go b/internal/acceptance/openstack/clustering/v1/pkg.go similarity index 100% rename from acceptance/openstack/clustering/v1/pkg.go rename to internal/acceptance/openstack/clustering/v1/pkg.go diff --git a/acceptance/openstack/clustering/v1/policies_test.go b/internal/acceptance/openstack/clustering/v1/policies_test.go similarity index 92% rename from acceptance/openstack/clustering/v1/policies_test.go rename to internal/acceptance/openstack/clustering/v1/policies_test.go index a1fc2be6ef..9502b9a1af 100644 --- a/acceptance/openstack/clustering/v1/policies_test.go +++ b/internal/acceptance/openstack/clustering/v1/policies_test.go @@ -6,8 +6,8 @@ package v1 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/clustering/v1/policies" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/clustering/v1/policytypes_test.go b/internal/acceptance/openstack/clustering/v1/policytypes_test.go similarity index 92% rename from acceptance/openstack/clustering/v1/policytypes_test.go rename to internal/acceptance/openstack/clustering/v1/policytypes_test.go index 70a43f9c66..fe930b9c57 100644 --- a/acceptance/openstack/clustering/v1/policytypes_test.go +++ b/internal/acceptance/openstack/clustering/v1/policytypes_test.go @@ -6,8 +6,8 @@ package v1 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/clustering/v1/policytypes" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/clustering/v1/profiles_test.go b/internal/acceptance/openstack/clustering/v1/profiles_test.go similarity index 93% rename from acceptance/openstack/clustering/v1/profiles_test.go rename to internal/acceptance/openstack/clustering/v1/profiles_test.go index b65b7d4565..0c6143df57 100644 --- a/acceptance/openstack/clustering/v1/profiles_test.go +++ b/internal/acceptance/openstack/clustering/v1/profiles_test.go @@ -6,8 +6,8 @@ package v1 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/clustering/v1/profiles" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/clustering/v1/profiletypes_test.go b/internal/acceptance/openstack/clustering/v1/profiletypes_test.go similarity index 89% rename from acceptance/openstack/clustering/v1/profiletypes_test.go rename to internal/acceptance/openstack/clustering/v1/profiletypes_test.go index 9a7c700252..2fe7c0301c 100644 --- a/acceptance/openstack/clustering/v1/profiletypes_test.go +++ b/internal/acceptance/openstack/clustering/v1/profiletypes_test.go @@ -6,8 +6,8 @@ package v1 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/clustering/v1/profiletypes" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/clustering/v1/receivers_test.go b/internal/acceptance/openstack/clustering/v1/receivers_test.go similarity index 94% rename from acceptance/openstack/clustering/v1/receivers_test.go rename to internal/acceptance/openstack/clustering/v1/receivers_test.go index 56b862abbf..6c7149a489 100644 --- a/acceptance/openstack/clustering/v1/receivers_test.go +++ b/internal/acceptance/openstack/clustering/v1/receivers_test.go @@ -6,8 +6,8 @@ package v1 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/clustering/v1/receivers" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/clustering/v1/webhooktrigger_test.go b/internal/acceptance/openstack/clustering/v1/webhooktrigger_test.go similarity index 96% rename from acceptance/openstack/clustering/v1/webhooktrigger_test.go rename to internal/acceptance/openstack/clustering/v1/webhooktrigger_test.go index b4b4a2e37e..c481208711 100644 --- a/acceptance/openstack/clustering/v1/webhooktrigger_test.go +++ b/internal/acceptance/openstack/clustering/v1/webhooktrigger_test.go @@ -6,7 +6,7 @@ package v1 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" "github.com/gophercloud/gophercloud/openstack/clustering/v1/nodes" "github.com/gophercloud/gophercloud/openstack/clustering/v1/webhooks" th "github.com/gophercloud/gophercloud/testhelper" diff --git a/acceptance/openstack/common.go b/internal/acceptance/openstack/common.go similarity index 100% rename from acceptance/openstack/common.go rename to internal/acceptance/openstack/common.go diff --git a/acceptance/openstack/compute/v2/aggregates_test.go b/internal/acceptance/openstack/compute/v2/aggregates_test.go similarity index 96% rename from acceptance/openstack/compute/v2/aggregates_test.go rename to internal/acceptance/openstack/compute/v2/aggregates_test.go index a90a77b8dd..84e05dec52 100644 --- a/acceptance/openstack/compute/v2/aggregates_test.go +++ b/internal/acceptance/openstack/compute/v2/aggregates_test.go @@ -9,8 +9,8 @@ import ( "testing" "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/aggregates" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/hypervisors" th "github.com/gophercloud/gophercloud/testhelper" diff --git a/acceptance/openstack/compute/v2/attachinterfaces_test.go b/internal/acceptance/openstack/compute/v2/attachinterfaces_test.go similarity index 90% rename from acceptance/openstack/compute/v2/attachinterfaces_test.go rename to internal/acceptance/openstack/compute/v2/attachinterfaces_test.go index 3efef0c1d5..3887634f1e 100644 --- a/acceptance/openstack/compute/v2/attachinterfaces_test.go +++ b/internal/acceptance/openstack/compute/v2/attachinterfaces_test.go @@ -6,8 +6,8 @@ package v2 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/compute/v2/availabilityzones_test.go b/internal/acceptance/openstack/compute/v2/availabilityzones_test.go similarity index 91% rename from acceptance/openstack/compute/v2/availabilityzones_test.go rename to internal/acceptance/openstack/compute/v2/availabilityzones_test.go index 967d56f5ab..9d125589e6 100644 --- a/acceptance/openstack/compute/v2/availabilityzones_test.go +++ b/internal/acceptance/openstack/compute/v2/availabilityzones_test.go @@ -6,8 +6,8 @@ package v2 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/availabilityzones" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/compute/v2/bootfromvolume_test.go b/internal/acceptance/openstack/compute/v2/bootfromvolume_test.go similarity index 97% rename from acceptance/openstack/compute/v2/bootfromvolume_test.go rename to internal/acceptance/openstack/compute/v2/bootfromvolume_test.go index 3296c84627..2a054568ec 100644 --- a/acceptance/openstack/compute/v2/bootfromvolume_test.go +++ b/internal/acceptance/openstack/compute/v2/bootfromvolume_test.go @@ -6,9 +6,9 @@ package v2 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - blockstorage "github.com/gophercloud/gophercloud/acceptance/openstack/blockstorage/v2" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + blockstorage "github.com/gophercloud/gophercloud/internal/acceptance/openstack/blockstorage/v2" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/bootfromvolume" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/volumeattach" th "github.com/gophercloud/gophercloud/testhelper" diff --git a/acceptance/openstack/compute/v2/compute.go b/internal/acceptance/openstack/compute/v2/compute.go similarity index 99% rename from acceptance/openstack/compute/v2/compute.go rename to internal/acceptance/openstack/compute/v2/compute.go index 7a5c696e68..f3d102916e 100644 --- a/acceptance/openstack/compute/v2/compute.go +++ b/internal/acceptance/openstack/compute/v2/compute.go @@ -9,8 +9,8 @@ import ( "testing" "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/aggregates" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/attachinterfaces" diff --git a/acceptance/openstack/compute/v2/defsecrules_test.go b/internal/acceptance/openstack/compute/v2/defsecrules_test.go similarity index 91% rename from acceptance/openstack/compute/v2/defsecrules_test.go rename to internal/acceptance/openstack/compute/v2/defsecrules_test.go index cb0352f80d..b2e5accad0 100644 --- a/acceptance/openstack/compute/v2/defsecrules_test.go +++ b/internal/acceptance/openstack/compute/v2/defsecrules_test.go @@ -6,8 +6,8 @@ package v2 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" dsr "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/defsecrules" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/compute/v2/diagnostics_test.go b/internal/acceptance/openstack/compute/v2/diagnostics_test.go similarity index 83% rename from acceptance/openstack/compute/v2/diagnostics_test.go rename to internal/acceptance/openstack/compute/v2/diagnostics_test.go index a19d54fe66..0424bb27a3 100644 --- a/acceptance/openstack/compute/v2/diagnostics_test.go +++ b/internal/acceptance/openstack/compute/v2/diagnostics_test.go @@ -6,8 +6,8 @@ package v2 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/diagnostics" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/compute/v2/extension_test.go b/internal/acceptance/openstack/compute/v2/extension_test.go similarity index 88% rename from acceptance/openstack/compute/v2/extension_test.go rename to internal/acceptance/openstack/compute/v2/extension_test.go index 8fb0f28ba1..96b321a0c7 100644 --- a/acceptance/openstack/compute/v2/extension_test.go +++ b/internal/acceptance/openstack/compute/v2/extension_test.go @@ -6,8 +6,8 @@ package v2 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/common/extensions" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/compute/v2/flavors_test.go b/internal/acceptance/openstack/compute/v2/flavors_test.go similarity index 96% rename from acceptance/openstack/compute/v2/flavors_test.go rename to internal/acceptance/openstack/compute/v2/flavors_test.go index 9e3ec1db45..ec14e5ad33 100644 --- a/acceptance/openstack/compute/v2/flavors_test.go +++ b/internal/acceptance/openstack/compute/v2/flavors_test.go @@ -6,9 +6,9 @@ package v2 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - identity "github.com/gophercloud/gophercloud/acceptance/openstack/identity/v3" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + identity "github.com/gophercloud/gophercloud/internal/acceptance/openstack/identity/v3" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/compute/v2/flavors" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/compute/v2/floatingip_test.go b/internal/acceptance/openstack/compute/v2/floatingip_test.go similarity index 96% rename from acceptance/openstack/compute/v2/floatingip_test.go rename to internal/acceptance/openstack/compute/v2/floatingip_test.go index dae68e7b63..6e2ecb0399 100644 --- a/acceptance/openstack/compute/v2/floatingip_test.go +++ b/internal/acceptance/openstack/compute/v2/floatingip_test.go @@ -6,8 +6,8 @@ package v2 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/floatingips" "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" th "github.com/gophercloud/gophercloud/testhelper" diff --git a/acceptance/openstack/compute/v2/hypervisors_test.go b/internal/acceptance/openstack/compute/v2/hypervisors_test.go similarity index 95% rename from acceptance/openstack/compute/v2/hypervisors_test.go rename to internal/acceptance/openstack/compute/v2/hypervisors_test.go index ee4410726f..4992a62814 100644 --- a/acceptance/openstack/compute/v2/hypervisors_test.go +++ b/internal/acceptance/openstack/compute/v2/hypervisors_test.go @@ -8,8 +8,8 @@ import ( "testing" "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/hypervisors" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/compute/v2/images_test.go b/internal/acceptance/openstack/compute/v2/images_test.go similarity index 89% rename from acceptance/openstack/compute/v2/images_test.go rename to internal/acceptance/openstack/compute/v2/images_test.go index 4d25d29416..48dae1d974 100644 --- a/acceptance/openstack/compute/v2/images_test.go +++ b/internal/acceptance/openstack/compute/v2/images_test.go @@ -6,8 +6,8 @@ package v2 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/compute/v2/images" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/compute/v2/instance_actions_test.go b/internal/acceptance/openstack/compute/v2/instance_actions_test.go similarity index 94% rename from acceptance/openstack/compute/v2/instance_actions_test.go rename to internal/acceptance/openstack/compute/v2/instance_actions_test.go index 3b60d90067..9928c2ceb8 100644 --- a/acceptance/openstack/compute/v2/instance_actions_test.go +++ b/internal/acceptance/openstack/compute/v2/instance_actions_test.go @@ -7,8 +7,8 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/instanceactions" "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" th "github.com/gophercloud/gophercloud/testhelper" diff --git a/acceptance/openstack/compute/v2/keypairs_test.go b/internal/acceptance/openstack/compute/v2/keypairs_test.go similarity index 94% rename from acceptance/openstack/compute/v2/keypairs_test.go rename to internal/acceptance/openstack/compute/v2/keypairs_test.go index dd6c577963..21e37bd802 100644 --- a/acceptance/openstack/compute/v2/keypairs_test.go +++ b/internal/acceptance/openstack/compute/v2/keypairs_test.go @@ -6,9 +6,9 @@ package v2 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - identity "github.com/gophercloud/gophercloud/acceptance/openstack/identity/v3" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + identity "github.com/gophercloud/gophercloud/internal/acceptance/openstack/identity/v3" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs" "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" th "github.com/gophercloud/gophercloud/testhelper" diff --git a/acceptance/openstack/compute/v2/limits_test.go b/internal/acceptance/openstack/compute/v2/limits_test.go similarity index 90% rename from acceptance/openstack/compute/v2/limits_test.go rename to internal/acceptance/openstack/compute/v2/limits_test.go index 3aab23c3a7..c355ea0181 100644 --- a/acceptance/openstack/compute/v2/limits_test.go +++ b/internal/acceptance/openstack/compute/v2/limits_test.go @@ -7,8 +7,8 @@ import ( "strings" "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/limits" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/compute/v2/migrate_test.go b/internal/acceptance/openstack/compute/v2/migrate_test.go similarity index 95% rename from acceptance/openstack/compute/v2/migrate_test.go rename to internal/acceptance/openstack/compute/v2/migrate_test.go index fcc69e7fb9..8edc44b980 100644 --- a/acceptance/openstack/compute/v2/migrate_test.go +++ b/internal/acceptance/openstack/compute/v2/migrate_test.go @@ -6,7 +6,7 @@ package v2 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/migrate" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/compute/v2/network_test.go b/internal/acceptance/openstack/compute/v2/network_test.go similarity index 90% rename from acceptance/openstack/compute/v2/network_test.go rename to internal/acceptance/openstack/compute/v2/network_test.go index 345356830f..0850c040b0 100644 --- a/acceptance/openstack/compute/v2/network_test.go +++ b/internal/acceptance/openstack/compute/v2/network_test.go @@ -6,8 +6,8 @@ package v2 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/networks" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/compute/v2/pkg.go b/internal/acceptance/openstack/compute/v2/pkg.go similarity index 100% rename from acceptance/openstack/compute/v2/pkg.go rename to internal/acceptance/openstack/compute/v2/pkg.go diff --git a/acceptance/openstack/compute/v2/quotaset_test.go b/internal/acceptance/openstack/compute/v2/quotaset_test.go similarity index 97% rename from acceptance/openstack/compute/v2/quotaset_test.go rename to internal/acceptance/openstack/compute/v2/quotaset_test.go index 3273b81b22..72868e1d25 100644 --- a/acceptance/openstack/compute/v2/quotaset_test.go +++ b/internal/acceptance/openstack/compute/v2/quotaset_test.go @@ -9,8 +9,8 @@ import ( "testing" "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/quotasets" "github.com/gophercloud/gophercloud/openstack/identity/v3/projects" th "github.com/gophercloud/gophercloud/testhelper" diff --git a/acceptance/openstack/compute/v2/remoteconsoles_test.go b/internal/acceptance/openstack/compute/v2/remoteconsoles_test.go similarity index 81% rename from acceptance/openstack/compute/v2/remoteconsoles_test.go rename to internal/acceptance/openstack/compute/v2/remoteconsoles_test.go index 1a32de0045..f6fa7c8edb 100644 --- a/acceptance/openstack/compute/v2/remoteconsoles_test.go +++ b/internal/acceptance/openstack/compute/v2/remoteconsoles_test.go @@ -6,8 +6,8 @@ package v2 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/compute/v2/rescueunrescue_test.go b/internal/acceptance/openstack/compute/v2/rescueunrescue_test.go similarity index 89% rename from acceptance/openstack/compute/v2/rescueunrescue_test.go rename to internal/acceptance/openstack/compute/v2/rescueunrescue_test.go index b4304bfdfc..2d660a0b4f 100644 --- a/acceptance/openstack/compute/v2/rescueunrescue_test.go +++ b/internal/acceptance/openstack/compute/v2/rescueunrescue_test.go @@ -6,7 +6,7 @@ package v2 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/compute/v2/secgroup_test.go b/internal/acceptance/openstack/compute/v2/secgroup_test.go similarity index 96% rename from acceptance/openstack/compute/v2/secgroup_test.go rename to internal/acceptance/openstack/compute/v2/secgroup_test.go index 174c3f418f..aaa2595443 100644 --- a/acceptance/openstack/compute/v2/secgroup_test.go +++ b/internal/acceptance/openstack/compute/v2/secgroup_test.go @@ -6,8 +6,8 @@ package v2 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/secgroups" "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" th "github.com/gophercloud/gophercloud/testhelper" diff --git a/acceptance/openstack/compute/v2/servergroup_test.go b/internal/acceptance/openstack/compute/v2/servergroup_test.go similarity index 95% rename from acceptance/openstack/compute/v2/servergroup_test.go rename to internal/acceptance/openstack/compute/v2/servergroup_test.go index fe11c28a3f..2ee3a1494c 100644 --- a/acceptance/openstack/compute/v2/servergroup_test.go +++ b/internal/acceptance/openstack/compute/v2/servergroup_test.go @@ -6,8 +6,8 @@ package v2 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/servergroups" "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" th "github.com/gophercloud/gophercloud/testhelper" diff --git a/acceptance/openstack/compute/v2/servers_test.go b/internal/acceptance/openstack/compute/v2/servers_test.go similarity index 98% rename from acceptance/openstack/compute/v2/servers_test.go rename to internal/acceptance/openstack/compute/v2/servers_test.go index 953cb05720..0f32a4ea17 100644 --- a/acceptance/openstack/compute/v2/servers_test.go +++ b/internal/acceptance/openstack/compute/v2/servers_test.go @@ -8,9 +8,9 @@ import ( "testing" "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/acceptance/clients" - networks "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + networks "github.com/gophercloud/gophercloud/internal/acceptance/openstack/networking/v2" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/attachinterfaces" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/availabilityzones" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/extendedserverattributes" diff --git a/acceptance/openstack/compute/v2/services_test.go b/internal/acceptance/openstack/compute/v2/services_test.go similarity index 95% rename from acceptance/openstack/compute/v2/services_test.go rename to internal/acceptance/openstack/compute/v2/services_test.go index d7d36d7f2c..fbd8b760cf 100644 --- a/acceptance/openstack/compute/v2/services_test.go +++ b/internal/acceptance/openstack/compute/v2/services_test.go @@ -6,8 +6,8 @@ package v2 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/services" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/compute/v2/tenantnetworks_test.go b/internal/acceptance/openstack/compute/v2/tenantnetworks_test.go similarity index 90% rename from acceptance/openstack/compute/v2/tenantnetworks_test.go rename to internal/acceptance/openstack/compute/v2/tenantnetworks_test.go index f404663472..3700d5f38c 100644 --- a/acceptance/openstack/compute/v2/tenantnetworks_test.go +++ b/internal/acceptance/openstack/compute/v2/tenantnetworks_test.go @@ -6,8 +6,8 @@ package v2 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/tenantnetworks" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/compute/v2/usage_test.go b/internal/acceptance/openstack/compute/v2/usage_test.go similarity index 93% rename from acceptance/openstack/compute/v2/usage_test.go rename to internal/acceptance/openstack/compute/v2/usage_test.go index c4874a68c6..4ad35aa599 100644 --- a/acceptance/openstack/compute/v2/usage_test.go +++ b/internal/acceptance/openstack/compute/v2/usage_test.go @@ -8,8 +8,8 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/usage" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" diff --git a/acceptance/openstack/compute/v2/volumeattach_test.go b/internal/acceptance/openstack/compute/v2/volumeattach_test.go similarity index 81% rename from acceptance/openstack/compute/v2/volumeattach_test.go rename to internal/acceptance/openstack/compute/v2/volumeattach_test.go index 1d3dac08f8..11f5328e1f 100644 --- a/acceptance/openstack/compute/v2/volumeattach_test.go +++ b/internal/acceptance/openstack/compute/v2/volumeattach_test.go @@ -6,9 +6,9 @@ package v2 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - bs "github.com/gophercloud/gophercloud/acceptance/openstack/blockstorage/v2" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + bs "github.com/gophercloud/gophercloud/internal/acceptance/openstack/blockstorage/v2" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/container/v1/capsules.go b/internal/acceptance/openstack/container/v1/capsules.go similarity index 94% rename from acceptance/openstack/container/v1/capsules.go rename to internal/acceptance/openstack/container/v1/capsules.go index 08467ce2b9..7b4cdcd095 100644 --- a/acceptance/openstack/container/v1/capsules.go +++ b/internal/acceptance/openstack/container/v1/capsules.go @@ -4,7 +4,7 @@ import ( "fmt" "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/container/v1/capsules" ) diff --git a/acceptance/openstack/container/v1/capsules_test.go b/internal/acceptance/openstack/container/v1/capsules_test.go similarity index 97% rename from acceptance/openstack/container/v1/capsules_test.go rename to internal/acceptance/openstack/container/v1/capsules_test.go index 451d5e2853..4ebda2bbfd 100644 --- a/acceptance/openstack/container/v1/capsules_test.go +++ b/internal/acceptance/openstack/container/v1/capsules_test.go @@ -6,7 +6,7 @@ package v1 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" "github.com/gophercloud/gophercloud/openstack/container/v1/capsules" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" diff --git a/acceptance/openstack/container/v1/fixtures.go b/internal/acceptance/openstack/container/v1/fixtures.go similarity index 100% rename from acceptance/openstack/container/v1/fixtures.go rename to internal/acceptance/openstack/container/v1/fixtures.go diff --git a/acceptance/openstack/containerinfra/v1/certificates_test.go b/internal/acceptance/openstack/containerinfra/v1/certificates_test.go similarity index 96% rename from acceptance/openstack/containerinfra/v1/certificates_test.go rename to internal/acceptance/openstack/containerinfra/v1/certificates_test.go index b83f1ac28d..b1d1911cb6 100644 --- a/acceptance/openstack/containerinfra/v1/certificates_test.go +++ b/internal/acceptance/openstack/containerinfra/v1/certificates_test.go @@ -6,7 +6,7 @@ package v1 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" "github.com/gophercloud/gophercloud/openstack/containerinfra/v1/certificates" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/containerinfra/v1/clusters_test.go b/internal/acceptance/openstack/containerinfra/v1/clusters_test.go similarity index 94% rename from acceptance/openstack/containerinfra/v1/clusters_test.go rename to internal/acceptance/openstack/containerinfra/v1/clusters_test.go index 67eca18c53..1e2fbb6184 100644 --- a/acceptance/openstack/containerinfra/v1/clusters_test.go +++ b/internal/acceptance/openstack/containerinfra/v1/clusters_test.go @@ -7,8 +7,8 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/containerinfra/v1/clusters" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/containerinfra/v1/clustertemplates_test.go b/internal/acceptance/openstack/containerinfra/v1/clustertemplates_test.go similarity index 93% rename from acceptance/openstack/containerinfra/v1/clustertemplates_test.go rename to internal/acceptance/openstack/containerinfra/v1/clustertemplates_test.go index 87994c5c20..67843c1c4c 100644 --- a/acceptance/openstack/containerinfra/v1/clustertemplates_test.go +++ b/internal/acceptance/openstack/containerinfra/v1/clustertemplates_test.go @@ -6,8 +6,8 @@ package v1 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/containerinfra/v1/clustertemplates" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/containerinfra/v1/containerinfra.go b/internal/acceptance/openstack/containerinfra/v1/containerinfra.go similarity index 97% rename from acceptance/openstack/containerinfra/v1/containerinfra.go rename to internal/acceptance/openstack/containerinfra/v1/containerinfra.go index 796c0149ff..28127195f0 100644 --- a/acceptance/openstack/containerinfra/v1/containerinfra.go +++ b/internal/acceptance/openstack/containerinfra/v1/containerinfra.go @@ -8,9 +8,9 @@ import ( "time" "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/acceptance/clients" - idv3 "github.com/gophercloud/gophercloud/acceptance/openstack/identity/v3" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + idv3 "github.com/gophercloud/gophercloud/internal/acceptance/openstack/identity/v3" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/containerinfra/v1/clusters" "github.com/gophercloud/gophercloud/openstack/containerinfra/v1/clustertemplates" "github.com/gophercloud/gophercloud/openstack/containerinfra/v1/quotas" diff --git a/acceptance/openstack/containerinfra/v1/nodegroups_test.go b/internal/acceptance/openstack/containerinfra/v1/nodegroups_test.go similarity index 97% rename from acceptance/openstack/containerinfra/v1/nodegroups_test.go rename to internal/acceptance/openstack/containerinfra/v1/nodegroups_test.go index 7537d1d436..acbde956a5 100644 --- a/acceptance/openstack/containerinfra/v1/nodegroups_test.go +++ b/internal/acceptance/openstack/containerinfra/v1/nodegroups_test.go @@ -9,8 +9,8 @@ import ( "time" "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/containerinfra/v1/nodegroups" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/containerinfra/v1/pkg.go b/internal/acceptance/openstack/containerinfra/v1/pkg.go similarity index 100% rename from acceptance/openstack/containerinfra/v1/pkg.go rename to internal/acceptance/openstack/containerinfra/v1/pkg.go diff --git a/acceptance/openstack/containerinfra/v1/quotas_test.go b/internal/acceptance/openstack/containerinfra/v1/quotas_test.go similarity index 74% rename from acceptance/openstack/containerinfra/v1/quotas_test.go rename to internal/acceptance/openstack/containerinfra/v1/quotas_test.go index 5783d9195b..64169babca 100644 --- a/acceptance/openstack/containerinfra/v1/quotas_test.go +++ b/internal/acceptance/openstack/containerinfra/v1/quotas_test.go @@ -6,8 +6,8 @@ package v1 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/db/v1/configurations_test.go b/internal/acceptance/openstack/db/v1/configurations_test.go similarity index 94% rename from acceptance/openstack/db/v1/configurations_test.go rename to internal/acceptance/openstack/db/v1/configurations_test.go index 02472cc8fb..ae108aace3 100644 --- a/acceptance/openstack/db/v1/configurations_test.go +++ b/internal/acceptance/openstack/db/v1/configurations_test.go @@ -6,8 +6,8 @@ package v1 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/db/v1/configurations" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/db/v1/databases_test.go b/internal/acceptance/openstack/db/v1/databases_test.go similarity index 90% rename from acceptance/openstack/db/v1/databases_test.go rename to internal/acceptance/openstack/db/v1/databases_test.go index 854ca0bc0f..f8ca7d2250 100644 --- a/acceptance/openstack/db/v1/databases_test.go +++ b/internal/acceptance/openstack/db/v1/databases_test.go @@ -6,8 +6,8 @@ package v1 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/db/v1/databases" ) diff --git a/acceptance/openstack/db/v1/db.go b/internal/acceptance/openstack/db/v1/db.go similarity index 97% rename from acceptance/openstack/db/v1/db.go rename to internal/acceptance/openstack/db/v1/db.go index f5e637f3d9..1a1e39758e 100644 --- a/acceptance/openstack/db/v1/db.go +++ b/internal/acceptance/openstack/db/v1/db.go @@ -7,8 +7,8 @@ import ( "testing" "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/db/v1/databases" "github.com/gophercloud/gophercloud/openstack/db/v1/instances" "github.com/gophercloud/gophercloud/openstack/db/v1/users" diff --git a/acceptance/openstack/db/v1/flavors_test.go b/internal/acceptance/openstack/db/v1/flavors_test.go similarity index 90% rename from acceptance/openstack/db/v1/flavors_test.go rename to internal/acceptance/openstack/db/v1/flavors_test.go index 0c51565b60..2183e60f7a 100644 --- a/acceptance/openstack/db/v1/flavors_test.go +++ b/internal/acceptance/openstack/db/v1/flavors_test.go @@ -6,8 +6,8 @@ package v1 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/db/v1/flavors" ) diff --git a/acceptance/openstack/db/v1/instances_test.go b/internal/acceptance/openstack/db/v1/instances_test.go similarity index 92% rename from acceptance/openstack/db/v1/instances_test.go rename to internal/acceptance/openstack/db/v1/instances_test.go index 0d9ccdf08f..16a5d2d6df 100644 --- a/acceptance/openstack/db/v1/instances_test.go +++ b/internal/acceptance/openstack/db/v1/instances_test.go @@ -6,8 +6,8 @@ package v1 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/db/v1/instances" ) diff --git a/acceptance/openstack/db/v1/pkg.go b/internal/acceptance/openstack/db/v1/pkg.go similarity index 100% rename from acceptance/openstack/db/v1/pkg.go rename to internal/acceptance/openstack/db/v1/pkg.go diff --git a/acceptance/openstack/db/v1/users_test.go b/internal/acceptance/openstack/db/v1/users_test.go similarity index 89% rename from acceptance/openstack/db/v1/users_test.go rename to internal/acceptance/openstack/db/v1/users_test.go index 6bdc00bbad..c9417c06e8 100644 --- a/acceptance/openstack/db/v1/users_test.go +++ b/internal/acceptance/openstack/db/v1/users_test.go @@ -6,8 +6,8 @@ package v1 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/db/v1/users" ) diff --git a/acceptance/openstack/dns/v2/dns.go b/internal/acceptance/openstack/dns/v2/dns.go similarity index 99% rename from acceptance/openstack/dns/v2/dns.go rename to internal/acceptance/openstack/dns/v2/dns.go index 7fc34b70e0..d61588895f 100644 --- a/acceptance/openstack/dns/v2/dns.go +++ b/internal/acceptance/openstack/dns/v2/dns.go @@ -4,7 +4,7 @@ import ( "testing" "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/dns/v2/recordsets" transferAccepts "github.com/gophercloud/gophercloud/openstack/dns/v2/transfer/accept" transferRequests "github.com/gophercloud/gophercloud/openstack/dns/v2/transfer/request" diff --git a/acceptance/openstack/dns/v2/recordsets_test.go b/internal/acceptance/openstack/dns/v2/recordsets_test.go similarity index 94% rename from acceptance/openstack/dns/v2/recordsets_test.go rename to internal/acceptance/openstack/dns/v2/recordsets_test.go index 67b1f706ce..38aadcd6e8 100644 --- a/acceptance/openstack/dns/v2/recordsets_test.go +++ b/internal/acceptance/openstack/dns/v2/recordsets_test.go @@ -6,8 +6,8 @@ package v2 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/dns/v2/recordsets" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" diff --git a/acceptance/openstack/dns/v2/transfers_test.go b/internal/acceptance/openstack/dns/v2/transfers_test.go similarity index 92% rename from acceptance/openstack/dns/v2/transfers_test.go rename to internal/acceptance/openstack/dns/v2/transfers_test.go index 341aec6415..7cbcf4aa72 100644 --- a/acceptance/openstack/dns/v2/transfers_test.go +++ b/internal/acceptance/openstack/dns/v2/transfers_test.go @@ -6,9 +6,9 @@ package v2 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - identity "github.com/gophercloud/gophercloud/acceptance/openstack/identity/v3" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + identity "github.com/gophercloud/gophercloud/internal/acceptance/openstack/identity/v3" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" transferAccepts "github.com/gophercloud/gophercloud/openstack/dns/v2/transfer/accept" transferRequests "github.com/gophercloud/gophercloud/openstack/dns/v2/transfer/request" th "github.com/gophercloud/gophercloud/testhelper" diff --git a/acceptance/openstack/dns/v2/zones_test.go b/internal/acceptance/openstack/dns/v2/zones_test.go similarity index 88% rename from acceptance/openstack/dns/v2/zones_test.go rename to internal/acceptance/openstack/dns/v2/zones_test.go index e21b96d27c..b8edc55b10 100644 --- a/acceptance/openstack/dns/v2/zones_test.go +++ b/internal/acceptance/openstack/dns/v2/zones_test.go @@ -6,8 +6,8 @@ package v2 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/dns/v2/zones" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/identity/v2/extension_test.go b/internal/acceptance/openstack/identity/v2/extension_test.go similarity index 88% rename from acceptance/openstack/identity/v2/extension_test.go rename to internal/acceptance/openstack/identity/v2/extension_test.go index 7077c08a84..ed4202c372 100644 --- a/acceptance/openstack/identity/v2/extension_test.go +++ b/internal/acceptance/openstack/identity/v2/extension_test.go @@ -6,8 +6,8 @@ package v2 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/identity/v2/extensions" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/identity/v2/identity.go b/internal/acceptance/openstack/identity/v2/identity.go similarity index 98% rename from acceptance/openstack/identity/v2/identity.go rename to internal/acceptance/openstack/identity/v2/identity.go index f74812193b..6666e2e950 100644 --- a/acceptance/openstack/identity/v2/identity.go +++ b/internal/acceptance/openstack/identity/v2/identity.go @@ -6,7 +6,7 @@ import ( "testing" "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/identity/v2/extensions/admin/roles" "github.com/gophercloud/gophercloud/openstack/identity/v2/tenants" "github.com/gophercloud/gophercloud/openstack/identity/v2/users" diff --git a/acceptance/openstack/identity/v2/pkg.go b/internal/acceptance/openstack/identity/v2/pkg.go similarity index 100% rename from acceptance/openstack/identity/v2/pkg.go rename to internal/acceptance/openstack/identity/v2/pkg.go diff --git a/acceptance/openstack/identity/v2/role_test.go b/internal/acceptance/openstack/identity/v2/role_test.go similarity index 92% rename from acceptance/openstack/identity/v2/role_test.go rename to internal/acceptance/openstack/identity/v2/role_test.go index 4c40e70162..573209f4b8 100644 --- a/acceptance/openstack/identity/v2/role_test.go +++ b/internal/acceptance/openstack/identity/v2/role_test.go @@ -6,8 +6,8 @@ package v2 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/identity/v2/extensions/admin/roles" "github.com/gophercloud/gophercloud/openstack/identity/v2/users" th "github.com/gophercloud/gophercloud/testhelper" diff --git a/acceptance/openstack/identity/v2/tenant_test.go b/internal/acceptance/openstack/identity/v2/tenant_test.go similarity index 91% rename from acceptance/openstack/identity/v2/tenant_test.go rename to internal/acceptance/openstack/identity/v2/tenant_test.go index df2dcb7eb0..0b108f6733 100644 --- a/acceptance/openstack/identity/v2/tenant_test.go +++ b/internal/acceptance/openstack/identity/v2/tenant_test.go @@ -6,8 +6,8 @@ package v2 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/identity/v2/tenants" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/identity/v2/token_test.go b/internal/acceptance/openstack/identity/v2/token_test.go similarity index 91% rename from acceptance/openstack/identity/v2/token_test.go rename to internal/acceptance/openstack/identity/v2/token_test.go index cf758c60e2..085a3a5066 100644 --- a/acceptance/openstack/identity/v2/token_test.go +++ b/internal/acceptance/openstack/identity/v2/token_test.go @@ -6,8 +6,8 @@ package v2 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack" "github.com/gophercloud/gophercloud/openstack/identity/v2/tokens" th "github.com/gophercloud/gophercloud/testhelper" diff --git a/acceptance/openstack/identity/v2/user_test.go b/internal/acceptance/openstack/identity/v2/user_test.go similarity index 90% rename from acceptance/openstack/identity/v2/user_test.go rename to internal/acceptance/openstack/identity/v2/user_test.go index e700cdd3ec..d014fc9698 100644 --- a/acceptance/openstack/identity/v2/user_test.go +++ b/internal/acceptance/openstack/identity/v2/user_test.go @@ -6,8 +6,8 @@ package v2 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/identity/v2/users" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/identity/v3/applicationcredentials_test.go b/internal/acceptance/openstack/identity/v3/applicationcredentials_test.go similarity index 98% rename from acceptance/openstack/identity/v3/applicationcredentials_test.go rename to internal/acceptance/openstack/identity/v3/applicationcredentials_test.go index e1758c5b2f..e2c5d6580d 100644 --- a/acceptance/openstack/identity/v3/applicationcredentials_test.go +++ b/internal/acceptance/openstack/identity/v3/applicationcredentials_test.go @@ -7,8 +7,8 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack" "github.com/gophercloud/gophercloud/openstack/identity/v3/applicationcredentials" "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" diff --git a/acceptance/openstack/identity/v3/catalog_test.go b/internal/acceptance/openstack/identity/v3/catalog_test.go similarity index 79% rename from acceptance/openstack/identity/v3/catalog_test.go rename to internal/acceptance/openstack/identity/v3/catalog_test.go index 11542c58e6..c366b40793 100644 --- a/acceptance/openstack/identity/v3/catalog_test.go +++ b/internal/acceptance/openstack/identity/v3/catalog_test.go @@ -3,8 +3,8 @@ package v3 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/identity/v3/catalog" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/identity/v3/credentials_test.go b/internal/acceptance/openstack/identity/v3/credentials_test.go similarity index 97% rename from acceptance/openstack/identity/v3/credentials_test.go rename to internal/acceptance/openstack/identity/v3/credentials_test.go index acb5cd94f1..a0a6d675e6 100644 --- a/acceptance/openstack/identity/v3/credentials_test.go +++ b/internal/acceptance/openstack/identity/v3/credentials_test.go @@ -6,8 +6,8 @@ package v3 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack" "github.com/gophercloud/gophercloud/openstack/identity/v3/credentials" "github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/ec2tokens" diff --git a/acceptance/openstack/identity/v3/domains_test.go b/internal/acceptance/openstack/identity/v3/domains_test.go similarity index 94% rename from acceptance/openstack/identity/v3/domains_test.go rename to internal/acceptance/openstack/identity/v3/domains_test.go index 5e9d06b266..0a096ea7d9 100644 --- a/acceptance/openstack/identity/v3/domains_test.go +++ b/internal/acceptance/openstack/identity/v3/domains_test.go @@ -6,8 +6,8 @@ package v3 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/identity/v3/domains" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/identity/v3/ec2credentials_test.go b/internal/acceptance/openstack/identity/v3/ec2credentials_test.go similarity index 95% rename from acceptance/openstack/identity/v3/ec2credentials_test.go rename to internal/acceptance/openstack/identity/v3/ec2credentials_test.go index 5c19459f9f..36977de3e7 100644 --- a/acceptance/openstack/identity/v3/ec2credentials_test.go +++ b/internal/acceptance/openstack/identity/v3/ec2credentials_test.go @@ -6,8 +6,8 @@ package v3 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack" "github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/ec2credentials" "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" diff --git a/acceptance/openstack/identity/v3/endpoint_test.go b/internal/acceptance/openstack/identity/v3/endpoint_test.go similarity index 93% rename from acceptance/openstack/identity/v3/endpoint_test.go rename to internal/acceptance/openstack/identity/v3/endpoint_test.go index eac753d217..f0dbd37737 100644 --- a/acceptance/openstack/identity/v3/endpoint_test.go +++ b/internal/acceptance/openstack/identity/v3/endpoint_test.go @@ -8,8 +8,8 @@ import ( "testing" "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/identity/v3/endpoints" "github.com/gophercloud/gophercloud/openstack/identity/v3/services" th "github.com/gophercloud/gophercloud/testhelper" diff --git a/acceptance/openstack/identity/v3/federation_test.go b/internal/acceptance/openstack/identity/v3/federation_test.go similarity index 95% rename from acceptance/openstack/identity/v3/federation_test.go rename to internal/acceptance/openstack/identity/v3/federation_test.go index a1b286b9f0..eb84beda31 100644 --- a/acceptance/openstack/identity/v3/federation_test.go +++ b/internal/acceptance/openstack/identity/v3/federation_test.go @@ -7,8 +7,8 @@ import ( "testing" "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/federation" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/identity/v3/groups_test.go b/internal/acceptance/openstack/identity/v3/groups_test.go similarity index 95% rename from acceptance/openstack/identity/v3/groups_test.go rename to internal/acceptance/openstack/identity/v3/groups_test.go index b07d7d1b6c..ee16600be4 100644 --- a/acceptance/openstack/identity/v3/groups_test.go +++ b/internal/acceptance/openstack/identity/v3/groups_test.go @@ -6,8 +6,8 @@ package v3 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/identity/v3/groups" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/identity/v3/identity.go b/internal/acceptance/openstack/identity/v3/identity.go similarity index 99% rename from acceptance/openstack/identity/v3/identity.go rename to internal/acceptance/openstack/identity/v3/identity.go index 3824b2aa0a..c4c5282280 100644 --- a/acceptance/openstack/identity/v3/identity.go +++ b/internal/acceptance/openstack/identity/v3/identity.go @@ -4,7 +4,7 @@ import ( "testing" "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/identity/v3/domains" "github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/trusts" "github.com/gophercloud/gophercloud/openstack/identity/v3/groups" diff --git a/acceptance/openstack/identity/v3/limits_test.go b/internal/acceptance/openstack/identity/v3/limits_test.go similarity index 97% rename from acceptance/openstack/identity/v3/limits_test.go rename to internal/acceptance/openstack/identity/v3/limits_test.go index 05ef790829..af36e3b420 100644 --- a/acceptance/openstack/identity/v3/limits_test.go +++ b/internal/acceptance/openstack/identity/v3/limits_test.go @@ -7,8 +7,8 @@ import ( "os" "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/identity/v3/limits" "github.com/gophercloud/gophercloud/openstack/identity/v3/registeredlimits" "github.com/gophercloud/gophercloud/openstack/identity/v3/services" diff --git a/acceptance/openstack/identity/v3/oauth1_test.go b/internal/acceptance/openstack/identity/v3/oauth1_test.go similarity index 98% rename from acceptance/openstack/identity/v3/oauth1_test.go rename to internal/acceptance/openstack/identity/v3/oauth1_test.go index 0a8dd3c0ee..7270518478 100644 --- a/acceptance/openstack/identity/v3/oauth1_test.go +++ b/internal/acceptance/openstack/identity/v3/oauth1_test.go @@ -7,8 +7,8 @@ import ( "testing" "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack" "github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/oauth1" "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" diff --git a/acceptance/openstack/identity/v3/osinherit_test.go b/internal/acceptance/openstack/identity/v3/osinherit_test.go similarity index 99% rename from acceptance/openstack/identity/v3/osinherit_test.go rename to internal/acceptance/openstack/identity/v3/osinherit_test.go index 6ac777c0ec..b75171e08b 100644 --- a/acceptance/openstack/identity/v3/osinherit_test.go +++ b/internal/acceptance/openstack/identity/v3/osinherit_test.go @@ -7,7 +7,7 @@ import ( "testing" "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" "github.com/gophercloud/gophercloud/openstack/identity/v3/domains" "github.com/gophercloud/gophercloud/openstack/identity/v3/groups" "github.com/gophercloud/gophercloud/openstack/identity/v3/osinherit" diff --git a/acceptance/openstack/identity/v3/pkg.go b/internal/acceptance/openstack/identity/v3/pkg.go similarity index 100% rename from acceptance/openstack/identity/v3/pkg.go rename to internal/acceptance/openstack/identity/v3/pkg.go diff --git a/acceptance/openstack/identity/v3/policies_test.go b/internal/acceptance/openstack/identity/v3/policies_test.go similarity index 96% rename from acceptance/openstack/identity/v3/policies_test.go rename to internal/acceptance/openstack/identity/v3/policies_test.go index 01367018b8..39d43e08a9 100644 --- a/acceptance/openstack/identity/v3/policies_test.go +++ b/internal/acceptance/openstack/identity/v3/policies_test.go @@ -6,8 +6,8 @@ package v3 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/identity/v3/policies" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/identity/v3/projectendpoint_test.go b/internal/acceptance/openstack/identity/v3/projectendpoint_test.go similarity index 91% rename from acceptance/openstack/identity/v3/projectendpoint_test.go rename to internal/acceptance/openstack/identity/v3/projectendpoint_test.go index e8c5770a72..af4542d553 100644 --- a/acceptance/openstack/identity/v3/projectendpoint_test.go +++ b/internal/acceptance/openstack/identity/v3/projectendpoint_test.go @@ -6,8 +6,8 @@ package v3 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/identity/v3/endpoints" "github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/projectendpoints" th "github.com/gophercloud/gophercloud/testhelper" diff --git a/acceptance/openstack/identity/v3/projects_test.go b/internal/acceptance/openstack/identity/v3/projects_test.go similarity index 98% rename from acceptance/openstack/identity/v3/projects_test.go rename to internal/acceptance/openstack/identity/v3/projects_test.go index 4c23a45988..08265e1595 100644 --- a/acceptance/openstack/identity/v3/projects_test.go +++ b/internal/acceptance/openstack/identity/v3/projects_test.go @@ -6,8 +6,8 @@ package v3 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/identity/v3/projects" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/identity/v3/reauth_test.go b/internal/acceptance/openstack/identity/v3/reauth_test.go similarity index 92% rename from acceptance/openstack/identity/v3/reauth_test.go rename to internal/acceptance/openstack/identity/v3/reauth_test.go index f34a997adb..0c1b74991d 100644 --- a/acceptance/openstack/identity/v3/reauth_test.go +++ b/internal/acceptance/openstack/identity/v3/reauth_test.go @@ -6,7 +6,7 @@ package v3 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" "github.com/gophercloud/gophercloud/openstack" th "github.com/gophercloud/gophercloud/testhelper" diff --git a/acceptance/openstack/identity/v3/regions_test.go b/internal/acceptance/openstack/identity/v3/regions_test.go similarity index 94% rename from acceptance/openstack/identity/v3/regions_test.go rename to internal/acceptance/openstack/identity/v3/regions_test.go index 8bc9d41f02..3b1173555d 100644 --- a/acceptance/openstack/identity/v3/regions_test.go +++ b/internal/acceptance/openstack/identity/v3/regions_test.go @@ -6,8 +6,8 @@ package v3 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/identity/v3/regions" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/identity/v3/registeredlimits_test.go b/internal/acceptance/openstack/identity/v3/registeredlimits_test.go similarity index 96% rename from acceptance/openstack/identity/v3/registeredlimits_test.go rename to internal/acceptance/openstack/identity/v3/registeredlimits_test.go index 8fd24f4a5a..fa3f910559 100644 --- a/acceptance/openstack/identity/v3/registeredlimits_test.go +++ b/internal/acceptance/openstack/identity/v3/registeredlimits_test.go @@ -7,8 +7,8 @@ import ( "os" "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/identity/v3/registeredlimits" "github.com/gophercloud/gophercloud/openstack/identity/v3/services" th "github.com/gophercloud/gophercloud/testhelper" diff --git a/acceptance/openstack/identity/v3/roles_test.go b/internal/acceptance/openstack/identity/v3/roles_test.go similarity index 99% rename from acceptance/openstack/identity/v3/roles_test.go rename to internal/acceptance/openstack/identity/v3/roles_test.go index f61bddf0a4..cd8f6c5523 100644 --- a/acceptance/openstack/identity/v3/roles_test.go +++ b/internal/acceptance/openstack/identity/v3/roles_test.go @@ -7,8 +7,8 @@ import ( "testing" "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/identity/v3/domains" "github.com/gophercloud/gophercloud/openstack/identity/v3/groups" "github.com/gophercloud/gophercloud/openstack/identity/v3/roles" diff --git a/acceptance/openstack/identity/v3/service_test.go b/internal/acceptance/openstack/identity/v3/service_test.go similarity index 92% rename from acceptance/openstack/identity/v3/service_test.go rename to internal/acceptance/openstack/identity/v3/service_test.go index 246d9d8ec3..38eee8a4b9 100644 --- a/acceptance/openstack/identity/v3/service_test.go +++ b/internal/acceptance/openstack/identity/v3/service_test.go @@ -6,8 +6,8 @@ package v3 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/identity/v3/services" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/identity/v3/token_test.go b/internal/acceptance/openstack/identity/v3/token_test.go similarity index 89% rename from acceptance/openstack/identity/v3/token_test.go rename to internal/acceptance/openstack/identity/v3/token_test.go index 85ae0eff67..e2d052d4d2 100644 --- a/acceptance/openstack/identity/v3/token_test.go +++ b/internal/acceptance/openstack/identity/v3/token_test.go @@ -6,8 +6,8 @@ package v3 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack" "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" th "github.com/gophercloud/gophercloud/testhelper" diff --git a/acceptance/openstack/identity/v3/trusts_test.go b/internal/acceptance/openstack/identity/v3/trusts_test.go similarity index 96% rename from acceptance/openstack/identity/v3/trusts_test.go rename to internal/acceptance/openstack/identity/v3/trusts_test.go index 66dd42d08c..63b5c1afb7 100644 --- a/acceptance/openstack/identity/v3/trusts_test.go +++ b/internal/acceptance/openstack/identity/v3/trusts_test.go @@ -7,8 +7,8 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack" "github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/trusts" "github.com/gophercloud/gophercloud/openstack/identity/v3/roles" diff --git a/acceptance/openstack/identity/v3/users_test.go b/internal/acceptance/openstack/identity/v3/users_test.go similarity index 98% rename from acceptance/openstack/identity/v3/users_test.go rename to internal/acceptance/openstack/identity/v3/users_test.go index a5edb00854..9379a20930 100644 --- a/acceptance/openstack/identity/v3/users_test.go +++ b/internal/acceptance/openstack/identity/v3/users_test.go @@ -6,8 +6,8 @@ package v3 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/identity/v3/groups" "github.com/gophercloud/gophercloud/openstack/identity/v3/projects" "github.com/gophercloud/gophercloud/openstack/identity/v3/users" diff --git a/acceptance/openstack/imageservice/v2/imagedata_test.go b/internal/acceptance/openstack/imageservice/v2/imagedata_test.go similarity index 82% rename from acceptance/openstack/imageservice/v2/imagedata_test.go rename to internal/acceptance/openstack/imageservice/v2/imagedata_test.go index d19c38d4a9..d5c1c6f260 100644 --- a/acceptance/openstack/imageservice/v2/imagedata_test.go +++ b/internal/acceptance/openstack/imageservice/v2/imagedata_test.go @@ -3,8 +3,8 @@ package v2 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/imageservice/v2/imageimport_test.go b/internal/acceptance/openstack/imageservice/v2/imageimport_test.go similarity index 84% rename from acceptance/openstack/imageservice/v2/imageimport_test.go rename to internal/acceptance/openstack/imageservice/v2/imageimport_test.go index 53dbea4d3d..4d032dbfb3 100644 --- a/acceptance/openstack/imageservice/v2/imageimport_test.go +++ b/internal/acceptance/openstack/imageservice/v2/imageimport_test.go @@ -6,8 +6,8 @@ package v2 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/imageservice/v2/images_test.go b/internal/acceptance/openstack/imageservice/v2/images_test.go similarity index 97% rename from acceptance/openstack/imageservice/v2/images_test.go rename to internal/acceptance/openstack/imageservice/v2/images_test.go index ac6008cef8..a904471c5d 100644 --- a/acceptance/openstack/imageservice/v2/images_test.go +++ b/internal/acceptance/openstack/imageservice/v2/images_test.go @@ -8,8 +8,8 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/imageservice/v2/images" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" diff --git a/acceptance/openstack/imageservice/v2/imageservice.go b/internal/acceptance/openstack/imageservice/v2/imageservice.go similarity index 98% rename from acceptance/openstack/imageservice/v2/imageservice.go rename to internal/acceptance/openstack/imageservice/v2/imageservice.go index fd55741aa6..f093fe3123 100644 --- a/acceptance/openstack/imageservice/v2/imageservice.go +++ b/internal/acceptance/openstack/imageservice/v2/imageservice.go @@ -9,7 +9,7 @@ import ( "testing" "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/imageservice/v2/imagedata" "github.com/gophercloud/gophercloud/openstack/imageservice/v2/imageimport" "github.com/gophercloud/gophercloud/openstack/imageservice/v2/images" diff --git a/acceptance/openstack/imageservice/v2/tasks_test.go b/internal/acceptance/openstack/imageservice/v2/tasks_test.go similarity index 91% rename from acceptance/openstack/imageservice/v2/tasks_test.go rename to internal/acceptance/openstack/imageservice/v2/tasks_test.go index b06980c761..e751118797 100644 --- a/acceptance/openstack/imageservice/v2/tasks_test.go +++ b/internal/acceptance/openstack/imageservice/v2/tasks_test.go @@ -6,8 +6,8 @@ package v2 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/imageservice/v2/tasks" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" diff --git a/acceptance/openstack/keymanager/v1/acls_test.go b/internal/acceptance/openstack/keymanager/v1/acls_test.go similarity index 96% rename from acceptance/openstack/keymanager/v1/acls_test.go rename to internal/acceptance/openstack/keymanager/v1/acls_test.go index 5defa5b78e..5638443078 100644 --- a/acceptance/openstack/keymanager/v1/acls_test.go +++ b/internal/acceptance/openstack/keymanager/v1/acls_test.go @@ -6,8 +6,8 @@ package v1 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/keymanager/v1/acls" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/keymanager/v1/containers_test.go b/internal/acceptance/openstack/keymanager/v1/containers_test.go similarity index 97% rename from acceptance/openstack/keymanager/v1/containers_test.go rename to internal/acceptance/openstack/keymanager/v1/containers_test.go index b4699b3dee..cf7e162f8f 100644 --- a/acceptance/openstack/keymanager/v1/containers_test.go +++ b/internal/acceptance/openstack/keymanager/v1/containers_test.go @@ -6,8 +6,8 @@ package v1 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/keymanager/v1/containers" "github.com/gophercloud/gophercloud/openstack/keymanager/v1/secrets" th "github.com/gophercloud/gophercloud/testhelper" diff --git a/acceptance/openstack/keymanager/v1/keymanager.go b/internal/acceptance/openstack/keymanager/v1/keymanager.go similarity index 99% rename from acceptance/openstack/keymanager/v1/keymanager.go rename to internal/acceptance/openstack/keymanager/v1/keymanager.go index e4b6f95015..c0e9dd2728 100644 --- a/acceptance/openstack/keymanager/v1/keymanager.go +++ b/internal/acceptance/openstack/keymanager/v1/keymanager.go @@ -15,7 +15,7 @@ import ( "time" "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/keymanager/v1/containers" "github.com/gophercloud/gophercloud/openstack/keymanager/v1/orders" "github.com/gophercloud/gophercloud/openstack/keymanager/v1/secrets" diff --git a/acceptance/openstack/keymanager/v1/orders_test.go b/internal/acceptance/openstack/keymanager/v1/orders_test.go similarity index 94% rename from acceptance/openstack/keymanager/v1/orders_test.go rename to internal/acceptance/openstack/keymanager/v1/orders_test.go index cb9095af4f..f300f8fa7e 100644 --- a/acceptance/openstack/keymanager/v1/orders_test.go +++ b/internal/acceptance/openstack/keymanager/v1/orders_test.go @@ -6,8 +6,8 @@ package v1 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/keymanager/v1/containers" "github.com/gophercloud/gophercloud/openstack/keymanager/v1/orders" "github.com/gophercloud/gophercloud/openstack/keymanager/v1/secrets" diff --git a/acceptance/openstack/keymanager/v1/secrets_test.go b/internal/acceptance/openstack/keymanager/v1/secrets_test.go similarity index 98% rename from acceptance/openstack/keymanager/v1/secrets_test.go rename to internal/acceptance/openstack/keymanager/v1/secrets_test.go index 761ffc1338..59c989bb2e 100644 --- a/acceptance/openstack/keymanager/v1/secrets_test.go +++ b/internal/acceptance/openstack/keymanager/v1/secrets_test.go @@ -7,8 +7,8 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/keymanager/v1/secrets" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/loadbalancer/v2/amphorae_test.go b/internal/acceptance/openstack/loadbalancer/v2/amphorae_test.go similarity index 84% rename from acceptance/openstack/loadbalancer/v2/amphorae_test.go rename to internal/acceptance/openstack/loadbalancer/v2/amphorae_test.go index 42c14300bb..b2ec529e3f 100644 --- a/acceptance/openstack/loadbalancer/v2/amphorae_test.go +++ b/internal/acceptance/openstack/loadbalancer/v2/amphorae_test.go @@ -6,8 +6,8 @@ package v2 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/amphorae" ) diff --git a/acceptance/openstack/loadbalancer/v2/l7policies_test.go b/internal/acceptance/openstack/loadbalancer/v2/l7policies_test.go similarity index 85% rename from acceptance/openstack/loadbalancer/v2/l7policies_test.go rename to internal/acceptance/openstack/loadbalancer/v2/l7policies_test.go index 3ad3c9755b..66c6422d6e 100644 --- a/acceptance/openstack/loadbalancer/v2/l7policies_test.go +++ b/internal/acceptance/openstack/loadbalancer/v2/l7policies_test.go @@ -6,8 +6,8 @@ package v2 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/l7policies" ) diff --git a/acceptance/openstack/loadbalancer/v2/listeners_test.go b/internal/acceptance/openstack/loadbalancer/v2/listeners_test.go similarity index 85% rename from acceptance/openstack/loadbalancer/v2/listeners_test.go rename to internal/acceptance/openstack/loadbalancer/v2/listeners_test.go index ff06f4af41..a760224ee8 100644 --- a/acceptance/openstack/loadbalancer/v2/listeners_test.go +++ b/internal/acceptance/openstack/loadbalancer/v2/listeners_test.go @@ -6,8 +6,8 @@ package v2 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/listeners" ) diff --git a/acceptance/openstack/loadbalancer/v2/loadbalancer.go b/internal/acceptance/openstack/loadbalancer/v2/loadbalancer.go similarity index 99% rename from acceptance/openstack/loadbalancer/v2/loadbalancer.go rename to internal/acceptance/openstack/loadbalancer/v2/loadbalancer.go index eb11d68c17..72bd638ad0 100644 --- a/acceptance/openstack/loadbalancer/v2/loadbalancer.go +++ b/internal/acceptance/openstack/loadbalancer/v2/loadbalancer.go @@ -6,8 +6,8 @@ import ( "testing" "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/l7policies" "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/listeners" "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/loadbalancers" diff --git a/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go b/internal/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go similarity index 98% rename from acceptance/openstack/loadbalancer/v2/loadbalancers_test.go rename to internal/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go index 2a987bd9b9..f18f0abdae 100644 --- a/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go +++ b/internal/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go @@ -6,10 +6,10 @@ package v2 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - networking "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2" - "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2/extensions/qos/policies" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + networking "github.com/gophercloud/gophercloud/internal/acceptance/openstack/networking/v2" + "github.com/gophercloud/gophercloud/internal/acceptance/openstack/networking/v2/extensions/qos/policies" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/l7policies" "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/listeners" "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/loadbalancers" diff --git a/acceptance/openstack/loadbalancer/v2/monitors_test.go b/internal/acceptance/openstack/loadbalancer/v2/monitors_test.go similarity index 84% rename from acceptance/openstack/loadbalancer/v2/monitors_test.go rename to internal/acceptance/openstack/loadbalancer/v2/monitors_test.go index a721f51438..483b0cef51 100644 --- a/acceptance/openstack/loadbalancer/v2/monitors_test.go +++ b/internal/acceptance/openstack/loadbalancer/v2/monitors_test.go @@ -6,8 +6,8 @@ package v2 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/monitors" ) diff --git a/acceptance/openstack/loadbalancer/v2/pkg.go b/internal/acceptance/openstack/loadbalancer/v2/pkg.go similarity index 100% rename from acceptance/openstack/loadbalancer/v2/pkg.go rename to internal/acceptance/openstack/loadbalancer/v2/pkg.go diff --git a/acceptance/openstack/loadbalancer/v2/pools_test.go b/internal/acceptance/openstack/loadbalancer/v2/pools_test.go similarity index 84% rename from acceptance/openstack/loadbalancer/v2/pools_test.go rename to internal/acceptance/openstack/loadbalancer/v2/pools_test.go index 8a9724dd94..9ae691f9e8 100644 --- a/acceptance/openstack/loadbalancer/v2/pools_test.go +++ b/internal/acceptance/openstack/loadbalancer/v2/pools_test.go @@ -6,8 +6,8 @@ package v2 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/pools" ) diff --git a/acceptance/openstack/loadbalancer/v2/providers_test.go b/internal/acceptance/openstack/loadbalancer/v2/providers_test.go similarity index 85% rename from acceptance/openstack/loadbalancer/v2/providers_test.go rename to internal/acceptance/openstack/loadbalancer/v2/providers_test.go index c65511bbf9..f71cb191c9 100644 --- a/acceptance/openstack/loadbalancer/v2/providers_test.go +++ b/internal/acceptance/openstack/loadbalancer/v2/providers_test.go @@ -6,8 +6,8 @@ package v2 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/providers" ) diff --git a/acceptance/openstack/loadbalancer/v2/quotas_test.go b/internal/acceptance/openstack/loadbalancer/v2/quotas_test.go similarity index 94% rename from acceptance/openstack/loadbalancer/v2/quotas_test.go rename to internal/acceptance/openstack/loadbalancer/v2/quotas_test.go index 35dcb2e54a..e88dca5b44 100644 --- a/acceptance/openstack/loadbalancer/v2/quotas_test.go +++ b/internal/acceptance/openstack/loadbalancer/v2/quotas_test.go @@ -10,8 +10,8 @@ import ( "testing" "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/quotas" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/messaging/v2/claims_test.go b/internal/acceptance/openstack/messaging/v2/claims_test.go similarity index 92% rename from acceptance/openstack/messaging/v2/claims_test.go rename to internal/acceptance/openstack/messaging/v2/claims_test.go index 32fa87862f..d08ed61b2a 100644 --- a/acceptance/openstack/messaging/v2/claims_test.go +++ b/internal/acceptance/openstack/messaging/v2/claims_test.go @@ -6,8 +6,8 @@ package v2 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/messaging/v2/claims" ) diff --git a/acceptance/openstack/messaging/v2/message_test.go b/internal/acceptance/openstack/messaging/v2/message_test.go similarity index 98% rename from acceptance/openstack/messaging/v2/message_test.go rename to internal/acceptance/openstack/messaging/v2/message_test.go index b562023c61..bef7c4b94a 100644 --- a/acceptance/openstack/messaging/v2/message_test.go +++ b/internal/acceptance/openstack/messaging/v2/message_test.go @@ -6,8 +6,8 @@ package v2 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/messaging/v2/messages" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" diff --git a/acceptance/openstack/messaging/v2/messaging.go b/internal/acceptance/openstack/messaging/v2/messaging.go similarity index 98% rename from acceptance/openstack/messaging/v2/messaging.go rename to internal/acceptance/openstack/messaging/v2/messaging.go index 09f2641e09..00513b7e24 100644 --- a/acceptance/openstack/messaging/v2/messaging.go +++ b/internal/acceptance/openstack/messaging/v2/messaging.go @@ -5,7 +5,7 @@ import ( "testing" "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/messaging/v2/claims" "github.com/gophercloud/gophercloud/openstack/messaging/v2/messages" "github.com/gophercloud/gophercloud/openstack/messaging/v2/queues" diff --git a/acceptance/openstack/messaging/v2/queue_test.go b/internal/acceptance/openstack/messaging/v2/queue_test.go similarity index 96% rename from acceptance/openstack/messaging/v2/queue_test.go rename to internal/acceptance/openstack/messaging/v2/queue_test.go index c34d104ef5..bdbc5885ec 100644 --- a/acceptance/openstack/messaging/v2/queue_test.go +++ b/internal/acceptance/openstack/messaging/v2/queue_test.go @@ -6,8 +6,8 @@ package v2 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/messaging/v2/queues" "github.com/gophercloud/gophercloud/pagination" ) diff --git a/acceptance/openstack/networking/v2/apiversion_test.go b/internal/acceptance/openstack/networking/v2/apiversion_test.go similarity index 90% rename from acceptance/openstack/networking/v2/apiversion_test.go rename to internal/acceptance/openstack/networking/v2/apiversion_test.go index 9ad3e9212b..ab89b438fd 100644 --- a/acceptance/openstack/networking/v2/apiversion_test.go +++ b/internal/acceptance/openstack/networking/v2/apiversion_test.go @@ -6,8 +6,8 @@ package v2 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/apiversions" ) diff --git a/acceptance/openstack/networking/v2/extension_test.go b/internal/acceptance/openstack/networking/v2/extension_test.go similarity index 88% rename from acceptance/openstack/networking/v2/extension_test.go rename to internal/acceptance/openstack/networking/v2/extension_test.go index 4fed3e1fd9..06c6c632c1 100644 --- a/acceptance/openstack/networking/v2/extension_test.go +++ b/internal/acceptance/openstack/networking/v2/extension_test.go @@ -6,8 +6,8 @@ package v2 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/common/extensions" ) diff --git a/acceptance/openstack/networking/v2/extensions/agents/agents_test.go b/internal/acceptance/openstack/networking/v2/extensions/agents/agents_test.go similarity index 94% rename from acceptance/openstack/networking/v2/extensions/agents/agents_test.go rename to internal/acceptance/openstack/networking/v2/extensions/agents/agents_test.go index db34e75a1c..b9186e3bde 100644 --- a/acceptance/openstack/networking/v2/extensions/agents/agents_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/agents/agents_test.go @@ -7,10 +7,10 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud/acceptance/clients" - networking "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2" - spk "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2/extensions/bgp/speakers" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + networking "github.com/gophercloud/gophercloud/internal/acceptance/openstack/networking/v2" + spk "github.com/gophercloud/gophercloud/internal/acceptance/openstack/networking/v2/extensions/bgp/speakers" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/agents" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/bgp/speakers" th "github.com/gophercloud/gophercloud/testhelper" diff --git a/acceptance/openstack/networking/v2/extensions/agents/doc.go b/internal/acceptance/openstack/networking/v2/extensions/agents/doc.go similarity index 100% rename from acceptance/openstack/networking/v2/extensions/agents/doc.go rename to internal/acceptance/openstack/networking/v2/extensions/agents/doc.go diff --git a/acceptance/openstack/networking/v2/extensions/attributestags_test.go b/internal/acceptance/openstack/networking/v2/extensions/attributestags_test.go similarity index 95% rename from acceptance/openstack/networking/v2/extensions/attributestags_test.go rename to internal/acceptance/openstack/networking/v2/extensions/attributestags_test.go index b30a10e894..f9843281a9 100644 --- a/acceptance/openstack/networking/v2/extensions/attributestags_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/attributestags_test.go @@ -9,9 +9,9 @@ import ( "testing" "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/acceptance/clients" - networking "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + networking "github.com/gophercloud/gophercloud/internal/acceptance/openstack/networking/v2" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/attributestags" "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" th "github.com/gophercloud/gophercloud/testhelper" diff --git a/acceptance/openstack/networking/v2/extensions/bgp/peers/bgppeers_test.go b/internal/acceptance/openstack/networking/v2/extensions/bgp/peers/bgppeers_test.go similarity index 92% rename from acceptance/openstack/networking/v2/extensions/bgp/peers/bgppeers_test.go rename to internal/acceptance/openstack/networking/v2/extensions/bgp/peers/bgppeers_test.go index 37a2e7f94a..28e790b901 100644 --- a/acceptance/openstack/networking/v2/extensions/bgp/peers/bgppeers_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/bgp/peers/bgppeers_test.go @@ -3,8 +3,8 @@ package peers import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/bgp/peers" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/networking/v2/extensions/bgp/peers/doc.go b/internal/acceptance/openstack/networking/v2/extensions/bgp/peers/doc.go similarity index 100% rename from acceptance/openstack/networking/v2/extensions/bgp/peers/doc.go rename to internal/acceptance/openstack/networking/v2/extensions/bgp/peers/doc.go diff --git a/acceptance/openstack/networking/v2/extensions/bgp/peers/peers.go b/internal/acceptance/openstack/networking/v2/extensions/bgp/peers/peers.go similarity index 93% rename from acceptance/openstack/networking/v2/extensions/bgp/peers/peers.go rename to internal/acceptance/openstack/networking/v2/extensions/bgp/peers/peers.go index cd0b102dab..b92a488edf 100644 --- a/acceptance/openstack/networking/v2/extensions/bgp/peers/peers.go +++ b/internal/acceptance/openstack/networking/v2/extensions/bgp/peers/peers.go @@ -4,7 +4,7 @@ import ( "testing" "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/bgp/peers" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/networking/v2/extensions/bgp/speakers/bgpspeakers_test.go b/internal/acceptance/openstack/networking/v2/extensions/bgp/speakers/bgpspeakers_test.go similarity index 92% rename from acceptance/openstack/networking/v2/extensions/bgp/speakers/bgpspeakers_test.go rename to internal/acceptance/openstack/networking/v2/extensions/bgp/speakers/bgpspeakers_test.go index 9a7a9f3cde..2f45251cf7 100644 --- a/acceptance/openstack/networking/v2/extensions/bgp/speakers/bgpspeakers_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/bgp/speakers/bgpspeakers_test.go @@ -3,10 +3,10 @@ package speakers import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - networking "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2" - ap "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2/extensions/bgp/peers" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + networking "github.com/gophercloud/gophercloud/internal/acceptance/openstack/networking/v2" + ap "github.com/gophercloud/gophercloud/internal/acceptance/openstack/networking/v2/extensions/bgp/peers" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/bgp/peers" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/bgp/speakers" th "github.com/gophercloud/gophercloud/testhelper" diff --git a/acceptance/openstack/networking/v2/extensions/bgp/speakers/doc.go b/internal/acceptance/openstack/networking/v2/extensions/bgp/speakers/doc.go similarity index 100% rename from acceptance/openstack/networking/v2/extensions/bgp/speakers/doc.go rename to internal/acceptance/openstack/networking/v2/extensions/bgp/speakers/doc.go diff --git a/acceptance/openstack/networking/v2/extensions/bgp/speakers/speakers.go b/internal/acceptance/openstack/networking/v2/extensions/bgp/speakers/speakers.go similarity index 95% rename from acceptance/openstack/networking/v2/extensions/bgp/speakers/speakers.go rename to internal/acceptance/openstack/networking/v2/extensions/bgp/speakers/speakers.go index 00b706c53e..9a04eae2a9 100644 --- a/acceptance/openstack/networking/v2/extensions/bgp/speakers/speakers.go +++ b/internal/acceptance/openstack/networking/v2/extensions/bgp/speakers/speakers.go @@ -5,7 +5,7 @@ import ( "testing" "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/bgp/speakers" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/networking/v2/extensions/dns/dns.go b/internal/acceptance/openstack/networking/v2/extensions/dns/dns.go similarity index 98% rename from acceptance/openstack/networking/v2/extensions/dns/dns.go rename to internal/acceptance/openstack/networking/v2/extensions/dns/dns.go index e6f5b2e20e..72fc944ecf 100644 --- a/acceptance/openstack/networking/v2/extensions/dns/dns.go +++ b/internal/acceptance/openstack/networking/v2/extensions/dns/dns.go @@ -4,7 +4,7 @@ import ( "testing" "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/dns" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/floatingips" "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" diff --git a/acceptance/openstack/networking/v2/extensions/dns/dns_test.go b/internal/acceptance/openstack/networking/v2/extensions/dns/dns_test.go similarity index 95% rename from acceptance/openstack/networking/v2/extensions/dns/dns_test.go rename to internal/acceptance/openstack/networking/v2/extensions/dns/dns_test.go index 005d470b23..654112fe74 100644 --- a/acceptance/openstack/networking/v2/extensions/dns/dns_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/dns/dns_test.go @@ -7,10 +7,10 @@ import ( "os" "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - networking "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2" - "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2/extensions/layer3" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + networking "github.com/gophercloud/gophercloud/internal/acceptance/openstack/networking/v2" + "github.com/gophercloud/gophercloud/internal/acceptance/openstack/networking/v2/extensions/layer3" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/common/extensions" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/dns" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/floatingips" diff --git a/acceptance/openstack/networking/v2/extensions/extensions.go b/internal/acceptance/openstack/networking/v2/extensions/extensions.go similarity index 98% rename from acceptance/openstack/networking/v2/extensions/extensions.go rename to internal/acceptance/openstack/networking/v2/extensions/extensions.go index fe2999d066..83f64bd6ad 100644 --- a/acceptance/openstack/networking/v2/extensions/extensions.go +++ b/internal/acceptance/openstack/networking/v2/extensions/extensions.go @@ -4,7 +4,7 @@ import ( "testing" "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/external" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/groups" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/rules" diff --git a/acceptance/openstack/networking/v2/extensions/fwaas/firewall_test.go b/internal/acceptance/openstack/networking/v2/extensions/fwaas/firewall_test.go similarity index 95% rename from acceptance/openstack/networking/v2/extensions/fwaas/firewall_test.go rename to internal/acceptance/openstack/networking/v2/extensions/fwaas/firewall_test.go index 95533af241..2a1079c8ed 100644 --- a/acceptance/openstack/networking/v2/extensions/fwaas/firewall_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/fwaas/firewall_test.go @@ -6,9 +6,9 @@ package fwaas import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - layer3 "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2/extensions/layer3" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + layer3 "github.com/gophercloud/gophercloud/internal/acceptance/openstack/networking/v2/extensions/layer3" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/firewalls" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/routerinsertion" th "github.com/gophercloud/gophercloud/testhelper" diff --git a/acceptance/openstack/networking/v2/extensions/fwaas/fwaas.go b/internal/acceptance/openstack/networking/v2/extensions/fwaas/fwaas.go similarity index 99% rename from acceptance/openstack/networking/v2/extensions/fwaas/fwaas.go rename to internal/acceptance/openstack/networking/v2/extensions/fwaas/fwaas.go index 5f5bde95ff..4169e84288 100644 --- a/acceptance/openstack/networking/v2/extensions/fwaas/fwaas.go +++ b/internal/acceptance/openstack/networking/v2/extensions/fwaas/fwaas.go @@ -6,7 +6,7 @@ import ( "testing" "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/firewalls" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/policies" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/routerinsertion" diff --git a/acceptance/openstack/networking/v2/extensions/fwaas/policy_test.go b/internal/acceptance/openstack/networking/v2/extensions/fwaas/policy_test.go similarity index 91% rename from acceptance/openstack/networking/v2/extensions/fwaas/policy_test.go rename to internal/acceptance/openstack/networking/v2/extensions/fwaas/policy_test.go index a10839b8f4..1a474e83cc 100644 --- a/acceptance/openstack/networking/v2/extensions/fwaas/policy_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/fwaas/policy_test.go @@ -6,8 +6,8 @@ package fwaas import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/policies" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/networking/v2/extensions/fwaas/rule_test.go b/internal/acceptance/openstack/networking/v2/extensions/fwaas/rule_test.go similarity index 90% rename from acceptance/openstack/networking/v2/extensions/fwaas/rule_test.go rename to internal/acceptance/openstack/networking/v2/extensions/fwaas/rule_test.go index 343211990e..59a4ad19f5 100644 --- a/acceptance/openstack/networking/v2/extensions/fwaas/rule_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/fwaas/rule_test.go @@ -6,8 +6,8 @@ package fwaas import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/rules" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/networking/v2/extensions/fwaas_v2/fwaas_v2.go b/internal/acceptance/openstack/networking/v2/extensions/fwaas_v2/fwaas_v2.go similarity index 98% rename from acceptance/openstack/networking/v2/extensions/fwaas_v2/fwaas_v2.go rename to internal/acceptance/openstack/networking/v2/extensions/fwaas_v2/fwaas_v2.go index 57a621a8bd..d99106bf35 100644 --- a/acceptance/openstack/networking/v2/extensions/fwaas_v2/fwaas_v2.go +++ b/internal/acceptance/openstack/networking/v2/extensions/fwaas_v2/fwaas_v2.go @@ -6,7 +6,7 @@ import ( "testing" "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas_v2/groups" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas_v2/policies" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas_v2/rules" diff --git a/acceptance/openstack/networking/v2/extensions/fwaas_v2/groups_test.go b/internal/acceptance/openstack/networking/v2/extensions/fwaas_v2/groups_test.go similarity index 96% rename from acceptance/openstack/networking/v2/extensions/fwaas_v2/groups_test.go rename to internal/acceptance/openstack/networking/v2/extensions/fwaas_v2/groups_test.go index 0183f3c8ab..c09f3ab130 100644 --- a/acceptance/openstack/networking/v2/extensions/fwaas_v2/groups_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/fwaas_v2/groups_test.go @@ -6,8 +6,8 @@ package fwaas_v2 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas_v2/groups" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/networking/v2/extensions/fwaas_v2/policy_test.go b/internal/acceptance/openstack/networking/v2/extensions/fwaas_v2/policy_test.go similarity index 94% rename from acceptance/openstack/networking/v2/extensions/fwaas_v2/policy_test.go rename to internal/acceptance/openstack/networking/v2/extensions/fwaas_v2/policy_test.go index b2cc55ae64..2a375f2584 100644 --- a/acceptance/openstack/networking/v2/extensions/fwaas_v2/policy_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/fwaas_v2/policy_test.go @@ -6,8 +6,8 @@ package fwaas_v2 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas_v2/policies" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/networking/v2/extensions/fwaas_v2/rule_test.go b/internal/acceptance/openstack/networking/v2/extensions/fwaas_v2/rule_test.go similarity index 93% rename from acceptance/openstack/networking/v2/extensions/fwaas_v2/rule_test.go rename to internal/acceptance/openstack/networking/v2/extensions/fwaas_v2/rule_test.go index 43549fd622..c6eb6d2332 100644 --- a/acceptance/openstack/networking/v2/extensions/fwaas_v2/rule_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/fwaas_v2/rule_test.go @@ -8,8 +8,8 @@ import ( "strconv" "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas_v2/rules" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/networking/v2/extensions/layer3/addressscopes_test.go b/internal/acceptance/openstack/networking/v2/extensions/layer3/addressscopes_test.go similarity index 91% rename from acceptance/openstack/networking/v2/extensions/layer3/addressscopes_test.go rename to internal/acceptance/openstack/networking/v2/extensions/layer3/addressscopes_test.go index 1687b148b3..6b4384422d 100644 --- a/acceptance/openstack/networking/v2/extensions/layer3/addressscopes_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/layer3/addressscopes_test.go @@ -6,8 +6,8 @@ package layer3 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/addressscopes" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/networking/v2/extensions/layer3/extraroutes_test.go b/internal/acceptance/openstack/networking/v2/extensions/layer3/extraroutes_test.go similarity index 92% rename from acceptance/openstack/networking/v2/extensions/layer3/extraroutes_test.go rename to internal/acceptance/openstack/networking/v2/extensions/layer3/extraroutes_test.go index b5b4c9180e..ccf1e545be 100644 --- a/acceptance/openstack/networking/v2/extensions/layer3/extraroutes_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/layer3/extraroutes_test.go @@ -8,9 +8,9 @@ import ( "net" "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - networking "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + networking "github.com/gophercloud/gophercloud/internal/acceptance/openstack/networking/v2" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/extraroutes" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/routers" th "github.com/gophercloud/gophercloud/testhelper" diff --git a/acceptance/openstack/networking/v2/extensions/layer3/floatingips_test.go b/internal/acceptance/openstack/networking/v2/extensions/layer3/floatingips_test.go similarity index 96% rename from acceptance/openstack/networking/v2/extensions/layer3/floatingips_test.go rename to internal/acceptance/openstack/networking/v2/extensions/layer3/floatingips_test.go index 81d1d18f05..93dec689fa 100644 --- a/acceptance/openstack/networking/v2/extensions/layer3/floatingips_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/layer3/floatingips_test.go @@ -6,9 +6,9 @@ package layer3 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - networking "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + networking "github.com/gophercloud/gophercloud/internal/acceptance/openstack/networking/v2" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/floatingips" "github.com/gophercloud/gophercloud/openstack/networking/v2/subnets" th "github.com/gophercloud/gophercloud/testhelper" diff --git a/acceptance/openstack/networking/v2/extensions/layer3/l3_scheduling_test.go b/internal/acceptance/openstack/networking/v2/extensions/layer3/l3_scheduling_test.go similarity index 92% rename from acceptance/openstack/networking/v2/extensions/layer3/l3_scheduling_test.go rename to internal/acceptance/openstack/networking/v2/extensions/layer3/l3_scheduling_test.go index ec32dfda11..34d91ca29c 100644 --- a/acceptance/openstack/networking/v2/extensions/layer3/l3_scheduling_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/layer3/l3_scheduling_test.go @@ -6,9 +6,9 @@ package layer3 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - networking "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + networking "github.com/gophercloud/gophercloud/internal/acceptance/openstack/networking/v2" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/common/extensions" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/agents" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/routers" diff --git a/acceptance/openstack/networking/v2/extensions/layer3/layer3.go b/internal/acceptance/openstack/networking/v2/extensions/layer3/layer3.go similarity index 98% rename from acceptance/openstack/networking/v2/extensions/layer3/layer3.go rename to internal/acceptance/openstack/networking/v2/extensions/layer3/layer3.go index 6c1cdca080..8e4f5e8590 100644 --- a/acceptance/openstack/networking/v2/extensions/layer3/layer3.go +++ b/internal/acceptance/openstack/networking/v2/extensions/layer3/layer3.go @@ -6,8 +6,8 @@ import ( "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/addressscopes" "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/floatingips" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/portforwarding" diff --git a/acceptance/openstack/networking/v2/extensions/layer3/portforwardings_test.go b/internal/acceptance/openstack/networking/v2/extensions/layer3/portforwardings_test.go similarity index 91% rename from acceptance/openstack/networking/v2/extensions/layer3/portforwardings_test.go rename to internal/acceptance/openstack/networking/v2/extensions/layer3/portforwardings_test.go index 0aa35e0d3b..f5ae90b252 100644 --- a/acceptance/openstack/networking/v2/extensions/layer3/portforwardings_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/layer3/portforwardings_test.go @@ -3,9 +3,9 @@ package layer3 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - networking "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + networking "github.com/gophercloud/gophercloud/internal/acceptance/openstack/networking/v2" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/floatingips" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/portforwarding" th "github.com/gophercloud/gophercloud/testhelper" diff --git a/acceptance/openstack/networking/v2/extensions/layer3/routers_test.go b/internal/acceptance/openstack/networking/v2/extensions/layer3/routers_test.go similarity index 96% rename from acceptance/openstack/networking/v2/extensions/layer3/routers_test.go rename to internal/acceptance/openstack/networking/v2/extensions/layer3/routers_test.go index c4bb5eb40e..91e5af616a 100644 --- a/acceptance/openstack/networking/v2/extensions/layer3/routers_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/layer3/routers_test.go @@ -6,9 +6,9 @@ package layer3 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - networking "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + networking "github.com/gophercloud/gophercloud/internal/acceptance/openstack/networking/v2" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/routers" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/networking/v2/extensions/lbaas/lbaas.go b/internal/acceptance/openstack/networking/v2/extensions/lbaas/lbaas.go similarity index 98% rename from acceptance/openstack/networking/v2/extensions/lbaas/lbaas.go rename to internal/acceptance/openstack/networking/v2/extensions/lbaas/lbaas.go index b31d3e5b42..90b54ef253 100644 --- a/acceptance/openstack/networking/v2/extensions/lbaas/lbaas.go +++ b/internal/acceptance/openstack/networking/v2/extensions/lbaas/lbaas.go @@ -5,7 +5,7 @@ import ( "testing" "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas/members" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas/monitors" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas/pools" diff --git a/acceptance/openstack/networking/v2/extensions/lbaas/members_test.go b/internal/acceptance/openstack/networking/v2/extensions/lbaas/members_test.go similarity index 90% rename from acceptance/openstack/networking/v2/extensions/lbaas/members_test.go rename to internal/acceptance/openstack/networking/v2/extensions/lbaas/members_test.go index 78be8832d4..93cdfe4984 100644 --- a/acceptance/openstack/networking/v2/extensions/lbaas/members_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/lbaas/members_test.go @@ -7,9 +7,9 @@ import ( "testing" "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/acceptance/clients" - networking "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + networking "github.com/gophercloud/gophercloud/internal/acceptance/openstack/networking/v2" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas/members" ) diff --git a/acceptance/openstack/networking/v2/extensions/lbaas/monitors_test.go b/internal/acceptance/openstack/networking/v2/extensions/lbaas/monitors_test.go similarity index 92% rename from acceptance/openstack/networking/v2/extensions/lbaas/monitors_test.go rename to internal/acceptance/openstack/networking/v2/extensions/lbaas/monitors_test.go index 27ea62158b..e871065e6c 100644 --- a/acceptance/openstack/networking/v2/extensions/lbaas/monitors_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/lbaas/monitors_test.go @@ -6,8 +6,8 @@ package lbaas import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas/monitors" ) diff --git a/acceptance/openstack/networking/v2/extensions/lbaas/pools_test.go b/internal/acceptance/openstack/networking/v2/extensions/lbaas/pools_test.go similarity index 93% rename from acceptance/openstack/networking/v2/extensions/lbaas/pools_test.go rename to internal/acceptance/openstack/networking/v2/extensions/lbaas/pools_test.go index 0c8ba65d0c..4019b08ba3 100644 --- a/acceptance/openstack/networking/v2/extensions/lbaas/pools_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/lbaas/pools_test.go @@ -6,9 +6,9 @@ package lbaas import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - networking "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + networking "github.com/gophercloud/gophercloud/internal/acceptance/openstack/networking/v2" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas/pools" ) diff --git a/acceptance/openstack/networking/v2/extensions/lbaas/vips_test.go b/internal/acceptance/openstack/networking/v2/extensions/lbaas/vips_test.go similarity index 90% rename from acceptance/openstack/networking/v2/extensions/lbaas/vips_test.go rename to internal/acceptance/openstack/networking/v2/extensions/lbaas/vips_test.go index a954f25e28..3ae32d445e 100644 --- a/acceptance/openstack/networking/v2/extensions/lbaas/vips_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/lbaas/vips_test.go @@ -6,9 +6,9 @@ package lbaas import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - networking "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + networking "github.com/gophercloud/gophercloud/internal/acceptance/openstack/networking/v2" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas/vips" ) diff --git a/acceptance/openstack/networking/v2/extensions/lbaas_v2/l7policies_test.go b/internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/l7policies_test.go similarity index 86% rename from acceptance/openstack/networking/v2/extensions/lbaas_v2/l7policies_test.go rename to internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/l7policies_test.go index aa69b9e448..4aa1fa9aea 100644 --- a/acceptance/openstack/networking/v2/extensions/lbaas_v2/l7policies_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/l7policies_test.go @@ -6,8 +6,8 @@ package lbaas_v2 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/l7policies" ) diff --git a/acceptance/openstack/networking/v2/extensions/lbaas_v2/lbaas_v2.go b/internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/lbaas_v2.go similarity index 99% rename from acceptance/openstack/networking/v2/extensions/lbaas_v2/lbaas_v2.go rename to internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/lbaas_v2.go index 13fddaf803..897d66c568 100644 --- a/acceptance/openstack/networking/v2/extensions/lbaas_v2/lbaas_v2.go +++ b/internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/lbaas_v2.go @@ -6,7 +6,7 @@ import ( "testing" "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/l7policies" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/listeners" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/loadbalancers" diff --git a/acceptance/openstack/networking/v2/extensions/lbaas_v2/listeners_test.go b/internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/listeners_test.go similarity index 86% rename from acceptance/openstack/networking/v2/extensions/lbaas_v2/listeners_test.go rename to internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/listeners_test.go index 72d4b72282..2feb6d9ace 100644 --- a/acceptance/openstack/networking/v2/extensions/lbaas_v2/listeners_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/listeners_test.go @@ -6,8 +6,8 @@ package lbaas_v2 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/listeners" ) diff --git a/acceptance/openstack/networking/v2/extensions/lbaas_v2/loadbalancers_test.go b/internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/loadbalancers_test.go similarity index 97% rename from acceptance/openstack/networking/v2/extensions/lbaas_v2/loadbalancers_test.go rename to internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/loadbalancers_test.go index 8529deb957..11ee1f8ce0 100644 --- a/acceptance/openstack/networking/v2/extensions/lbaas_v2/loadbalancers_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/loadbalancers_test.go @@ -6,9 +6,9 @@ package lbaas_v2 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - networking "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + networking "github.com/gophercloud/gophercloud/internal/acceptance/openstack/networking/v2" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/l7policies" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/listeners" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/loadbalancers" diff --git a/acceptance/openstack/networking/v2/extensions/lbaas_v2/monitors_test.go b/internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/monitors_test.go similarity index 86% rename from acceptance/openstack/networking/v2/extensions/lbaas_v2/monitors_test.go rename to internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/monitors_test.go index 18e145bc90..2450bfd687 100644 --- a/acceptance/openstack/networking/v2/extensions/lbaas_v2/monitors_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/monitors_test.go @@ -6,8 +6,8 @@ package lbaas_v2 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/monitors" ) diff --git a/acceptance/openstack/networking/v2/extensions/lbaas_v2/pools_test.go b/internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/pools_test.go similarity index 86% rename from acceptance/openstack/networking/v2/extensions/lbaas_v2/pools_test.go rename to internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/pools_test.go index 69c4f6e1fc..efc2978cbd 100644 --- a/acceptance/openstack/networking/v2/extensions/lbaas_v2/pools_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/pools_test.go @@ -6,8 +6,8 @@ package lbaas_v2 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/pools" ) diff --git a/acceptance/openstack/networking/v2/extensions/mtu/mtu.go b/internal/acceptance/openstack/networking/v2/extensions/mtu/mtu.go similarity index 96% rename from acceptance/openstack/networking/v2/extensions/mtu/mtu.go rename to internal/acceptance/openstack/networking/v2/extensions/mtu/mtu.go index 36c06f9e8d..a02053f724 100644 --- a/acceptance/openstack/networking/v2/extensions/mtu/mtu.go +++ b/internal/acceptance/openstack/networking/v2/extensions/mtu/mtu.go @@ -4,7 +4,7 @@ import ( "testing" "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/mtu" "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" th "github.com/gophercloud/gophercloud/testhelper" diff --git a/acceptance/openstack/networking/v2/extensions/mtu/mtu_test.go b/internal/acceptance/openstack/networking/v2/extensions/mtu/mtu_test.go similarity index 94% rename from acceptance/openstack/networking/v2/extensions/mtu/mtu_test.go rename to internal/acceptance/openstack/networking/v2/extensions/mtu/mtu_test.go index 8264d2d504..7b10c99434 100644 --- a/acceptance/openstack/networking/v2/extensions/mtu/mtu_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/mtu/mtu_test.go @@ -6,9 +6,9 @@ package mtu import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - networking "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + networking "github.com/gophercloud/gophercloud/internal/acceptance/openstack/networking/v2" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/common/extensions" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/mtu" "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" diff --git a/acceptance/openstack/networking/v2/extensions/networkipavailabilities/networkipavailabilities_test.go b/internal/acceptance/openstack/networking/v2/extensions/networkipavailabilities/networkipavailabilities_test.go similarity index 87% rename from acceptance/openstack/networking/v2/extensions/networkipavailabilities/networkipavailabilities_test.go rename to internal/acceptance/openstack/networking/v2/extensions/networkipavailabilities/networkipavailabilities_test.go index 14b7106e29..e0bcebc1f7 100644 --- a/acceptance/openstack/networking/v2/extensions/networkipavailabilities/networkipavailabilities_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/networkipavailabilities/networkipavailabilities_test.go @@ -6,8 +6,8 @@ package networkipavailabilities import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/networkipavailabilities" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding.go b/internal/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding.go similarity index 96% rename from acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding.go rename to internal/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding.go index 2957176311..607cf1cd9c 100644 --- a/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding.go +++ b/internal/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding.go @@ -4,7 +4,7 @@ import ( "testing" "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/portsbinding" "github.com/gophercloud/gophercloud/openstack/networking/v2/ports" th "github.com/gophercloud/gophercloud/testhelper" diff --git a/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding_test.go b/internal/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding_test.go similarity index 90% rename from acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding_test.go rename to internal/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding_test.go index 8ab86b9ca9..50f5e8c99a 100644 --- a/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding_test.go @@ -6,9 +6,9 @@ package portsbinding import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - networking "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + networking "github.com/gophercloud/gophercloud/internal/acceptance/openstack/networking/v2" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/portsbinding" "github.com/gophercloud/gophercloud/openstack/networking/v2/ports" th "github.com/gophercloud/gophercloud/testhelper" diff --git a/acceptance/openstack/networking/v2/extensions/provider_test.go b/internal/acceptance/openstack/networking/v2/extensions/provider_test.go similarity index 75% rename from acceptance/openstack/networking/v2/extensions/provider_test.go rename to internal/acceptance/openstack/networking/v2/extensions/provider_test.go index 9e8f622d47..6b21141b9a 100644 --- a/acceptance/openstack/networking/v2/extensions/provider_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/provider_test.go @@ -6,9 +6,9 @@ package extensions import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - networking "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + networking "github.com/gophercloud/gophercloud/internal/acceptance/openstack/networking/v2" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/networking/v2/extensions/qos/policies/policies.go b/internal/acceptance/openstack/networking/v2/extensions/qos/policies/policies.go similarity index 95% rename from acceptance/openstack/networking/v2/extensions/qos/policies/policies.go rename to internal/acceptance/openstack/networking/v2/extensions/qos/policies/policies.go index 88b3228df9..91d4a398bc 100644 --- a/acceptance/openstack/networking/v2/extensions/qos/policies/policies.go +++ b/internal/acceptance/openstack/networking/v2/extensions/qos/policies/policies.go @@ -4,7 +4,7 @@ import ( "testing" "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/qos/policies" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/networking/v2/extensions/qos/policies/policies_test.go b/internal/acceptance/openstack/networking/v2/extensions/qos/policies/policies_test.go similarity index 92% rename from acceptance/openstack/networking/v2/extensions/qos/policies/policies_test.go rename to internal/acceptance/openstack/networking/v2/extensions/qos/policies/policies_test.go index 7dcbd68f1c..4bda08b2d0 100644 --- a/acceptance/openstack/networking/v2/extensions/qos/policies/policies_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/qos/policies/policies_test.go @@ -6,8 +6,8 @@ package policies import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/common/extensions" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/qos/policies" th "github.com/gophercloud/gophercloud/testhelper" diff --git a/acceptance/openstack/networking/v2/extensions/qos/rules/rules.go b/internal/acceptance/openstack/networking/v2/extensions/qos/rules/rules.go similarity index 100% rename from acceptance/openstack/networking/v2/extensions/qos/rules/rules.go rename to internal/acceptance/openstack/networking/v2/extensions/qos/rules/rules.go diff --git a/acceptance/openstack/networking/v2/extensions/qos/rules/rules_test.go b/internal/acceptance/openstack/networking/v2/extensions/qos/rules/rules_test.go similarity index 94% rename from acceptance/openstack/networking/v2/extensions/qos/rules/rules_test.go rename to internal/acceptance/openstack/networking/v2/extensions/qos/rules/rules_test.go index 43cfaaedea..8c88e31b0f 100644 --- a/acceptance/openstack/networking/v2/extensions/qos/rules/rules_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/qos/rules/rules_test.go @@ -3,9 +3,9 @@ package rules import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - accpolicies "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2/extensions/qos/policies" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + accpolicies "github.com/gophercloud/gophercloud/internal/acceptance/openstack/networking/v2/extensions/qos/policies" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/common/extensions" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/qos/policies" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/qos/rules" diff --git a/acceptance/openstack/networking/v2/extensions/qos/ruletypes/ruletypes_test.go b/internal/acceptance/openstack/networking/v2/extensions/qos/ruletypes/ruletypes_test.go similarity index 89% rename from acceptance/openstack/networking/v2/extensions/qos/ruletypes/ruletypes_test.go rename to internal/acceptance/openstack/networking/v2/extensions/qos/ruletypes/ruletypes_test.go index baa44ca05f..df89bff06e 100644 --- a/acceptance/openstack/networking/v2/extensions/qos/ruletypes/ruletypes_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/qos/ruletypes/ruletypes_test.go @@ -3,8 +3,8 @@ package ruletypes import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/common/extensions" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/qos/ruletypes" ) diff --git a/acceptance/openstack/networking/v2/extensions/quotas/quotas.go b/internal/acceptance/openstack/networking/v2/extensions/quotas/quotas.go similarity index 100% rename from acceptance/openstack/networking/v2/extensions/quotas/quotas.go rename to internal/acceptance/openstack/networking/v2/extensions/quotas/quotas.go diff --git a/acceptance/openstack/networking/v2/extensions/quotas/quotas_test.go b/internal/acceptance/openstack/networking/v2/extensions/quotas/quotas_test.go similarity index 93% rename from acceptance/openstack/networking/v2/extensions/quotas/quotas_test.go rename to internal/acceptance/openstack/networking/v2/extensions/quotas/quotas_test.go index c462cd3338..5749c90433 100644 --- a/acceptance/openstack/networking/v2/extensions/quotas/quotas_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/quotas/quotas_test.go @@ -9,8 +9,8 @@ import ( "reflect" "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/quotas" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/networking/v2/extensions/rbacpolicies/rbacpolicies.go b/internal/acceptance/openstack/networking/v2/extensions/rbacpolicies/rbacpolicies.go similarity index 100% rename from acceptance/openstack/networking/v2/extensions/rbacpolicies/rbacpolicies.go rename to internal/acceptance/openstack/networking/v2/extensions/rbacpolicies/rbacpolicies.go diff --git a/acceptance/openstack/networking/v2/extensions/rbacpolicies/rbacpolicies_test.go b/internal/acceptance/openstack/networking/v2/extensions/rbacpolicies/rbacpolicies_test.go similarity index 87% rename from acceptance/openstack/networking/v2/extensions/rbacpolicies/rbacpolicies_test.go rename to internal/acceptance/openstack/networking/v2/extensions/rbacpolicies/rbacpolicies_test.go index 7b071cb304..8015610a67 100644 --- a/acceptance/openstack/networking/v2/extensions/rbacpolicies/rbacpolicies_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/rbacpolicies/rbacpolicies_test.go @@ -6,10 +6,10 @@ package rbacpolicies import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - projects "github.com/gophercloud/gophercloud/acceptance/openstack/identity/v3" - networking "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + projects "github.com/gophercloud/gophercloud/internal/acceptance/openstack/identity/v3" + networking "github.com/gophercloud/gophercloud/internal/acceptance/openstack/networking/v2" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/rbacpolicies" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/networking/v2/extensions/security_test.go b/internal/acceptance/openstack/networking/v2/extensions/security_test.go similarity index 90% rename from acceptance/openstack/networking/v2/extensions/security_test.go rename to internal/acceptance/openstack/networking/v2/extensions/security_test.go index 967177b8cb..aaf772e7d5 100644 --- a/acceptance/openstack/networking/v2/extensions/security_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/security_test.go @@ -6,9 +6,9 @@ package extensions import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - networking "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + networking "github.com/gophercloud/gophercloud/internal/acceptance/openstack/networking/v2" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/groups" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/networking/v2/extensions/subnetpools/subnetpools.go b/internal/acceptance/openstack/networking/v2/extensions/subnetpools/subnetpools.go similarity index 96% rename from acceptance/openstack/networking/v2/extensions/subnetpools/subnetpools.go rename to internal/acceptance/openstack/networking/v2/extensions/subnetpools/subnetpools.go index 6380264b10..a7370cc3d5 100644 --- a/acceptance/openstack/networking/v2/extensions/subnetpools/subnetpools.go +++ b/internal/acceptance/openstack/networking/v2/extensions/subnetpools/subnetpools.go @@ -4,7 +4,7 @@ import ( "testing" "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/subnetpools" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/networking/v2/extensions/subnetpools/subnetpools_test.go b/internal/acceptance/openstack/networking/v2/extensions/subnetpools/subnetpools_test.go similarity index 91% rename from acceptance/openstack/networking/v2/extensions/subnetpools/subnetpools_test.go rename to internal/acceptance/openstack/networking/v2/extensions/subnetpools/subnetpools_test.go index 0989300019..12301d2b07 100644 --- a/acceptance/openstack/networking/v2/extensions/subnetpools/subnetpools_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/subnetpools/subnetpools_test.go @@ -6,8 +6,8 @@ package v2 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/subnetpools" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/networking/v2/extensions/trunk_details/trunks_test.go b/internal/acceptance/openstack/networking/v2/extensions/trunk_details/trunks_test.go similarity index 93% rename from acceptance/openstack/networking/v2/extensions/trunk_details/trunks_test.go rename to internal/acceptance/openstack/networking/v2/extensions/trunk_details/trunks_test.go index 309d090ffa..d2d5855540 100644 --- a/acceptance/openstack/networking/v2/extensions/trunk_details/trunks_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/trunk_details/trunks_test.go @@ -6,9 +6,9 @@ package trunk_details import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - v2 "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2" - v2Trunks "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2/extensions/trunks" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + v2 "github.com/gophercloud/gophercloud/internal/acceptance/openstack/networking/v2" + v2Trunks "github.com/gophercloud/gophercloud/internal/acceptance/openstack/networking/v2/extensions/trunks" "github.com/gophercloud/gophercloud/openstack/common/extensions" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/trunk_details" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/trunks" diff --git a/acceptance/openstack/networking/v2/extensions/trunks/trunks.go b/internal/acceptance/openstack/networking/v2/extensions/trunks/trunks.go similarity index 95% rename from acceptance/openstack/networking/v2/extensions/trunks/trunks.go rename to internal/acceptance/openstack/networking/v2/extensions/trunks/trunks.go index 18fc920fd6..215120dae8 100644 --- a/acceptance/openstack/networking/v2/extensions/trunks/trunks.go +++ b/internal/acceptance/openstack/networking/v2/extensions/trunks/trunks.go @@ -4,7 +4,7 @@ import ( "testing" "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/trunks" ) diff --git a/acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go b/internal/acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go similarity index 97% rename from acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go rename to internal/acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go index 0e62059278..a41209c95c 100644 --- a/acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go @@ -7,9 +7,9 @@ import ( "sort" "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - v2 "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + v2 "github.com/gophercloud/gophercloud/internal/acceptance/openstack/networking/v2" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/common/extensions" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/attributestags" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/trunks" diff --git a/acceptance/openstack/networking/v2/extensions/vlantransparent/vlantransparent.go b/internal/acceptance/openstack/networking/v2/extensions/vlantransparent/vlantransparent.go similarity index 97% rename from acceptance/openstack/networking/v2/extensions/vlantransparent/vlantransparent.go rename to internal/acceptance/openstack/networking/v2/extensions/vlantransparent/vlantransparent.go index e91ada32d6..e061ea3813 100644 --- a/acceptance/openstack/networking/v2/extensions/vlantransparent/vlantransparent.go +++ b/internal/acceptance/openstack/networking/v2/extensions/vlantransparent/vlantransparent.go @@ -4,7 +4,7 @@ import ( "testing" "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/vlantransparent" "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" th "github.com/gophercloud/gophercloud/testhelper" diff --git a/acceptance/openstack/networking/v2/extensions/vlantransparent/vlantransparent_test.go b/internal/acceptance/openstack/networking/v2/extensions/vlantransparent/vlantransparent_test.go similarity index 85% rename from acceptance/openstack/networking/v2/extensions/vlantransparent/vlantransparent_test.go rename to internal/acceptance/openstack/networking/v2/extensions/vlantransparent/vlantransparent_test.go index ff5692752a..c919866309 100644 --- a/acceptance/openstack/networking/v2/extensions/vlantransparent/vlantransparent_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/vlantransparent/vlantransparent_test.go @@ -6,9 +6,9 @@ package v2 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - networkingv2 "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + networkingv2 "github.com/gophercloud/gophercloud/internal/acceptance/openstack/networking/v2" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/common/extensions" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/networking/v2/extensions/vpnaas/group_test.go b/internal/acceptance/openstack/networking/v2/extensions/vpnaas/group_test.go similarity index 91% rename from acceptance/openstack/networking/v2/extensions/vpnaas/group_test.go rename to internal/acceptance/openstack/networking/v2/extensions/vpnaas/group_test.go index fafd6df496..b65c356e81 100644 --- a/acceptance/openstack/networking/v2/extensions/vpnaas/group_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/vpnaas/group_test.go @@ -6,8 +6,8 @@ package vpnaas import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/vpnaas/endpointgroups" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/networking/v2/extensions/vpnaas/ikepolicy_test.go b/internal/acceptance/openstack/networking/v2/extensions/vpnaas/ikepolicy_test.go similarity index 91% rename from acceptance/openstack/networking/v2/extensions/vpnaas/ikepolicy_test.go rename to internal/acceptance/openstack/networking/v2/extensions/vpnaas/ikepolicy_test.go index 0c2715d9ab..f203c2f42e 100644 --- a/acceptance/openstack/networking/v2/extensions/vpnaas/ikepolicy_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/vpnaas/ikepolicy_test.go @@ -6,8 +6,8 @@ package vpnaas import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/vpnaas/ikepolicies" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/networking/v2/extensions/vpnaas/ipsecpolicy_test.go b/internal/acceptance/openstack/networking/v2/extensions/vpnaas/ipsecpolicy_test.go similarity index 90% rename from acceptance/openstack/networking/v2/extensions/vpnaas/ipsecpolicy_test.go rename to internal/acceptance/openstack/networking/v2/extensions/vpnaas/ipsecpolicy_test.go index d0d5729693..d36e1bff48 100644 --- a/acceptance/openstack/networking/v2/extensions/vpnaas/ipsecpolicy_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/vpnaas/ipsecpolicy_test.go @@ -6,8 +6,8 @@ package vpnaas import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/vpnaas/ipsecpolicies" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/networking/v2/extensions/vpnaas/service_test.go b/internal/acceptance/openstack/networking/v2/extensions/vpnaas/service_test.go similarity index 83% rename from acceptance/openstack/networking/v2/extensions/vpnaas/service_test.go rename to internal/acceptance/openstack/networking/v2/extensions/vpnaas/service_test.go index d8b7daa42b..17de845fa9 100644 --- a/acceptance/openstack/networking/v2/extensions/vpnaas/service_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/vpnaas/service_test.go @@ -6,9 +6,9 @@ package vpnaas import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - layer3 "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2/extensions/layer3" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + layer3 "github.com/gophercloud/gophercloud/internal/acceptance/openstack/networking/v2/extensions/layer3" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/vpnaas/services" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/networking/v2/extensions/vpnaas/siteconnection_test.go b/internal/acceptance/openstack/networking/v2/extensions/vpnaas/siteconnection_test.go similarity index 89% rename from acceptance/openstack/networking/v2/extensions/vpnaas/siteconnection_test.go rename to internal/acceptance/openstack/networking/v2/extensions/vpnaas/siteconnection_test.go index 5f8bf4bea0..9dab42c48c 100644 --- a/acceptance/openstack/networking/v2/extensions/vpnaas/siteconnection_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/vpnaas/siteconnection_test.go @@ -6,10 +6,10 @@ package vpnaas import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - networks "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2" - layer3 "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2/extensions/layer3" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + networks "github.com/gophercloud/gophercloud/internal/acceptance/openstack/networking/v2" + layer3 "github.com/gophercloud/gophercloud/internal/acceptance/openstack/networking/v2/extensions/layer3" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/routers" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/vpnaas/siteconnections" th "github.com/gophercloud/gophercloud/testhelper" diff --git a/acceptance/openstack/networking/v2/extensions/vpnaas/vpnaas.go b/internal/acceptance/openstack/networking/v2/extensions/vpnaas/vpnaas.go similarity index 99% rename from acceptance/openstack/networking/v2/extensions/vpnaas/vpnaas.go rename to internal/acceptance/openstack/networking/v2/extensions/vpnaas/vpnaas.go index 2ba6a09050..a165aa328a 100644 --- a/acceptance/openstack/networking/v2/extensions/vpnaas/vpnaas.go +++ b/internal/acceptance/openstack/networking/v2/extensions/vpnaas/vpnaas.go @@ -4,7 +4,7 @@ import ( "testing" "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/vpnaas/endpointgroups" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/vpnaas/ikepolicies" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/vpnaas/ipsecpolicies" diff --git a/acceptance/openstack/networking/v2/networking.go b/internal/acceptance/openstack/networking/v2/networking.go similarity index 99% rename from acceptance/openstack/networking/v2/networking.go rename to internal/acceptance/openstack/networking/v2/networking.go index aae456b198..6777b39dcb 100644 --- a/acceptance/openstack/networking/v2/networking.go +++ b/internal/acceptance/openstack/networking/v2/networking.go @@ -5,7 +5,7 @@ import ( "testing" "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/extradhcpopts" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/portsecurity" "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" diff --git a/acceptance/openstack/networking/v2/networks_test.go b/internal/acceptance/openstack/networking/v2/networks_test.go similarity index 97% rename from acceptance/openstack/networking/v2/networks_test.go rename to internal/acceptance/openstack/networking/v2/networks_test.go index cf8a88e60d..1208e058c8 100644 --- a/acceptance/openstack/networking/v2/networks_test.go +++ b/internal/acceptance/openstack/networking/v2/networks_test.go @@ -7,8 +7,8 @@ import ( "strings" "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/external" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/portsecurity" "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" diff --git a/acceptance/openstack/networking/v2/ports_test.go b/internal/acceptance/openstack/networking/v2/ports_test.go similarity index 98% rename from acceptance/openstack/networking/v2/ports_test.go rename to internal/acceptance/openstack/networking/v2/ports_test.go index 815a5c53b7..19151c2fe3 100644 --- a/acceptance/openstack/networking/v2/ports_test.go +++ b/internal/acceptance/openstack/networking/v2/ports_test.go @@ -7,9 +7,9 @@ import ( "strings" "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - extensions "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2/extensions" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + extensions "github.com/gophercloud/gophercloud/internal/acceptance/openstack/networking/v2/extensions" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/extradhcpopts" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/portsecurity" "github.com/gophercloud/gophercloud/openstack/networking/v2/ports" diff --git a/acceptance/openstack/networking/v2/subnets_test.go b/internal/acceptance/openstack/networking/v2/subnets_test.go similarity index 97% rename from acceptance/openstack/networking/v2/subnets_test.go rename to internal/acceptance/openstack/networking/v2/subnets_test.go index 6b45d7c6a0..e7f3b71ebb 100644 --- a/acceptance/openstack/networking/v2/subnets_test.go +++ b/internal/acceptance/openstack/networking/v2/subnets_test.go @@ -8,9 +8,9 @@ import ( "strings" "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - subnetpools "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2/extensions/subnetpools" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + subnetpools "github.com/gophercloud/gophercloud/internal/acceptance/openstack/networking/v2/extensions/subnetpools" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/subnets" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/objectstorage/v1/accounts_test.go b/internal/acceptance/openstack/objectstorage/v1/accounts_test.go similarity index 95% rename from acceptance/openstack/objectstorage/v1/accounts_test.go rename to internal/acceptance/openstack/objectstorage/v1/accounts_test.go index a484dc1601..b6792ce9f5 100644 --- a/acceptance/openstack/objectstorage/v1/accounts_test.go +++ b/internal/acceptance/openstack/objectstorage/v1/accounts_test.go @@ -7,7 +7,7 @@ import ( "strings" "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" "github.com/gophercloud/gophercloud/openstack/objectstorage/v1/accounts" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/objectstorage/v1/containers_test.go b/internal/acceptance/openstack/objectstorage/v1/containers_test.go similarity index 98% rename from acceptance/openstack/objectstorage/v1/containers_test.go rename to internal/acceptance/openstack/objectstorage/v1/containers_test.go index d754fe3a59..314b8019e7 100644 --- a/acceptance/openstack/objectstorage/v1/containers_test.go +++ b/internal/acceptance/openstack/objectstorage/v1/containers_test.go @@ -7,8 +7,8 @@ import ( "strings" "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/objectstorage/v1/containers" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" diff --git a/acceptance/openstack/objectstorage/v1/objects_test.go b/internal/acceptance/openstack/objectstorage/v1/objects_test.go similarity index 98% rename from acceptance/openstack/objectstorage/v1/objects_test.go rename to internal/acceptance/openstack/objectstorage/v1/objects_test.go index 0569428f3d..e60fba9178 100644 --- a/acceptance/openstack/objectstorage/v1/objects_test.go +++ b/internal/acceptance/openstack/objectstorage/v1/objects_test.go @@ -11,8 +11,8 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/objectstorage/v1/containers" "github.com/gophercloud/gophercloud/openstack/objectstorage/v1/objects" th "github.com/gophercloud/gophercloud/testhelper" diff --git a/acceptance/openstack/objectstorage/v1/pkg.go b/internal/acceptance/openstack/objectstorage/v1/pkg.go similarity index 100% rename from acceptance/openstack/objectstorage/v1/pkg.go rename to internal/acceptance/openstack/objectstorage/v1/pkg.go diff --git a/acceptance/openstack/objectstorage/v1/versioning_test.go b/internal/acceptance/openstack/objectstorage/v1/versioning_test.go similarity index 97% rename from acceptance/openstack/objectstorage/v1/versioning_test.go rename to internal/acceptance/openstack/objectstorage/v1/versioning_test.go index 30ebadecc6..2c90f09ec9 100644 --- a/acceptance/openstack/objectstorage/v1/versioning_test.go +++ b/internal/acceptance/openstack/objectstorage/v1/versioning_test.go @@ -7,8 +7,8 @@ import ( "strings" "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/objectstorage/v1/containers" "github.com/gophercloud/gophercloud/openstack/objectstorage/v1/objects" th "github.com/gophercloud/gophercloud/testhelper" diff --git a/acceptance/openstack/orchestration/v1/buildinfo_test.go b/internal/acceptance/openstack/orchestration/v1/buildinfo_test.go similarity index 86% rename from acceptance/openstack/orchestration/v1/buildinfo_test.go rename to internal/acceptance/openstack/orchestration/v1/buildinfo_test.go index cec6376f80..0bbea7be74 100644 --- a/acceptance/openstack/orchestration/v1/buildinfo_test.go +++ b/internal/acceptance/openstack/orchestration/v1/buildinfo_test.go @@ -6,7 +6,7 @@ package v1 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" "github.com/gophercloud/gophercloud/openstack/orchestration/v1/buildinfo" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/orchestration/v1/orchestration.go b/internal/acceptance/openstack/orchestration/v1/orchestration.go similarity index 98% rename from acceptance/openstack/orchestration/v1/orchestration.go rename to internal/acceptance/openstack/orchestration/v1/orchestration.go index b26b9a8c19..48c155505d 100644 --- a/acceptance/openstack/orchestration/v1/orchestration.go +++ b/internal/acceptance/openstack/orchestration/v1/orchestration.go @@ -5,7 +5,7 @@ import ( "testing" "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/orchestration/v1/stacks" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/orchestration/v1/stackevents_test.go b/internal/acceptance/openstack/orchestration/v1/stackevents_test.go similarity index 93% rename from acceptance/openstack/orchestration/v1/stackevents_test.go rename to internal/acceptance/openstack/orchestration/v1/stackevents_test.go index 150d51c9d8..a5de54486d 100644 --- a/acceptance/openstack/orchestration/v1/stackevents_test.go +++ b/internal/acceptance/openstack/orchestration/v1/stackevents_test.go @@ -6,7 +6,7 @@ package v1 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" "github.com/gophercloud/gophercloud/openstack/orchestration/v1/stackevents" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/orchestration/v1/stackresources_test.go b/internal/acceptance/openstack/orchestration/v1/stackresources_test.go similarity index 92% rename from acceptance/openstack/orchestration/v1/stackresources_test.go rename to internal/acceptance/openstack/orchestration/v1/stackresources_test.go index 29c20e92e7..2033419a1c 100644 --- a/acceptance/openstack/orchestration/v1/stackresources_test.go +++ b/internal/acceptance/openstack/orchestration/v1/stackresources_test.go @@ -6,8 +6,8 @@ package v1 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/orchestration/v1/stackresources" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/orchestration/v1/stacks_test.go b/internal/acceptance/openstack/orchestration/v1/stacks_test.go similarity index 90% rename from acceptance/openstack/orchestration/v1/stacks_test.go rename to internal/acceptance/openstack/orchestration/v1/stacks_test.go index f49818e4c3..02e4d57299 100644 --- a/acceptance/openstack/orchestration/v1/stacks_test.go +++ b/internal/acceptance/openstack/orchestration/v1/stacks_test.go @@ -6,8 +6,8 @@ package v1 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/orchestration/v1/stacks" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/orchestration/v1/stacktemplates_test.go b/internal/acceptance/openstack/orchestration/v1/stacktemplates_test.go similarity index 90% rename from acceptance/openstack/orchestration/v1/stacktemplates_test.go rename to internal/acceptance/openstack/orchestration/v1/stacktemplates_test.go index 7513bcb43f..738d3fda44 100644 --- a/acceptance/openstack/orchestration/v1/stacktemplates_test.go +++ b/internal/acceptance/openstack/orchestration/v1/stacktemplates_test.go @@ -6,8 +6,8 @@ package v1 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/orchestration/v1/stacktemplates" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/orchestration/v1/testdata/samplefile b/internal/acceptance/openstack/orchestration/v1/testdata/samplefile similarity index 100% rename from acceptance/openstack/orchestration/v1/testdata/samplefile rename to internal/acceptance/openstack/orchestration/v1/testdata/samplefile diff --git a/acceptance/openstack/pkg.go b/internal/acceptance/openstack/pkg.go similarity index 100% rename from acceptance/openstack/pkg.go rename to internal/acceptance/openstack/pkg.go diff --git a/acceptance/openstack/placement/v1/pkg.go b/internal/acceptance/openstack/placement/v1/pkg.go similarity index 100% rename from acceptance/openstack/placement/v1/pkg.go rename to internal/acceptance/openstack/placement/v1/pkg.go diff --git a/acceptance/openstack/placement/v1/placement.go b/internal/acceptance/openstack/placement/v1/placement.go similarity index 97% rename from acceptance/openstack/placement/v1/placement.go rename to internal/acceptance/openstack/placement/v1/placement.go index 3ae96328b6..3fc3cf9397 100644 --- a/acceptance/openstack/placement/v1/placement.go +++ b/internal/acceptance/openstack/placement/v1/placement.go @@ -4,7 +4,7 @@ import ( "testing" "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/placement/v1/resourceproviders" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/placement/v1/resourceproviders_test.go b/internal/acceptance/openstack/placement/v1/resourceproviders_test.go similarity index 96% rename from acceptance/openstack/placement/v1/resourceproviders_test.go rename to internal/acceptance/openstack/placement/v1/resourceproviders_test.go index ee915ea035..52c1140ff3 100644 --- a/acceptance/openstack/placement/v1/resourceproviders_test.go +++ b/internal/acceptance/openstack/placement/v1/resourceproviders_test.go @@ -3,8 +3,8 @@ package v1 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/placement/v1/resourceproviders" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/sharedfilesystems/v2/availabilityzones_test.go b/internal/acceptance/openstack/sharedfilesystems/v2/availabilityzones_test.go similarity index 91% rename from acceptance/openstack/sharedfilesystems/v2/availabilityzones_test.go rename to internal/acceptance/openstack/sharedfilesystems/v2/availabilityzones_test.go index ed42cdfef9..acb85406a0 100644 --- a/acceptance/openstack/sharedfilesystems/v2/availabilityzones_test.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/availabilityzones_test.go @@ -6,7 +6,7 @@ package v2 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/availabilityzones" ) diff --git a/acceptance/openstack/sharedfilesystems/v2/messages/messages.go b/internal/acceptance/openstack/sharedfilesystems/v2/messages/messages.go similarity index 100% rename from acceptance/openstack/sharedfilesystems/v2/messages/messages.go rename to internal/acceptance/openstack/sharedfilesystems/v2/messages/messages.go diff --git a/acceptance/openstack/sharedfilesystems/v2/messages/messages_test.go b/internal/acceptance/openstack/sharedfilesystems/v2/messages/messages_test.go similarity index 95% rename from acceptance/openstack/sharedfilesystems/v2/messages/messages_test.go rename to internal/acceptance/openstack/sharedfilesystems/v2/messages/messages_test.go index d3e65603d9..e3e92b0da0 100644 --- a/acceptance/openstack/sharedfilesystems/v2/messages/messages_test.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/messages/messages_test.go @@ -3,8 +3,8 @@ package messages import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/messages" ) diff --git a/acceptance/openstack/sharedfilesystems/v2/messages/pkg.go b/internal/acceptance/openstack/sharedfilesystems/v2/messages/pkg.go similarity index 100% rename from acceptance/openstack/sharedfilesystems/v2/messages/pkg.go rename to internal/acceptance/openstack/sharedfilesystems/v2/messages/pkg.go diff --git a/acceptance/openstack/sharedfilesystems/v2/pkg.go b/internal/acceptance/openstack/sharedfilesystems/v2/pkg.go similarity index 100% rename from acceptance/openstack/sharedfilesystems/v2/pkg.go rename to internal/acceptance/openstack/sharedfilesystems/v2/pkg.go diff --git a/acceptance/openstack/sharedfilesystems/v2/replicas.go b/internal/acceptance/openstack/sharedfilesystems/v2/replicas.go similarity index 98% rename from acceptance/openstack/sharedfilesystems/v2/replicas.go rename to internal/acceptance/openstack/sharedfilesystems/v2/replicas.go index 5756986c78..3c08152250 100644 --- a/acceptance/openstack/sharedfilesystems/v2/replicas.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/replicas.go @@ -5,7 +5,7 @@ import ( "strings" "testing" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/replicas" diff --git a/acceptance/openstack/sharedfilesystems/v2/replicas_test.go b/internal/acceptance/openstack/sharedfilesystems/v2/replicas_test.go similarity index 98% rename from acceptance/openstack/sharedfilesystems/v2/replicas_test.go rename to internal/acceptance/openstack/sharedfilesystems/v2/replicas_test.go index 6fa28cada6..1b54d79b60 100644 --- a/acceptance/openstack/sharedfilesystems/v2/replicas_test.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/replicas_test.go @@ -7,8 +7,8 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/replicas" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/sharedfilesystems/v2/schedulerstats_test.go b/internal/acceptance/openstack/sharedfilesystems/v2/schedulerstats_test.go similarity index 82% rename from acceptance/openstack/sharedfilesystems/v2/schedulerstats_test.go rename to internal/acceptance/openstack/sharedfilesystems/v2/schedulerstats_test.go index aebe388672..2d51348cab 100644 --- a/acceptance/openstack/sharedfilesystems/v2/schedulerstats_test.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/schedulerstats_test.go @@ -6,8 +6,8 @@ package v2 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/schedulerstats" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/sharedfilesystems/v2/securityservices.go b/internal/acceptance/openstack/sharedfilesystems/v2/securityservices.go similarity index 96% rename from acceptance/openstack/sharedfilesystems/v2/securityservices.go rename to internal/acceptance/openstack/sharedfilesystems/v2/securityservices.go index 342a91789e..47b93afb98 100644 --- a/acceptance/openstack/sharedfilesystems/v2/securityservices.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/securityservices.go @@ -4,7 +4,7 @@ import ( "testing" "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/securityservices" ) diff --git a/acceptance/openstack/sharedfilesystems/v2/securityservices_test.go b/internal/acceptance/openstack/sharedfilesystems/v2/securityservices_test.go similarity index 97% rename from acceptance/openstack/sharedfilesystems/v2/securityservices_test.go rename to internal/acceptance/openstack/sharedfilesystems/v2/securityservices_test.go index e0e1b16f68..e578c52e3e 100644 --- a/acceptance/openstack/sharedfilesystems/v2/securityservices_test.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/securityservices_test.go @@ -6,8 +6,8 @@ package v2 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/securityservices" ) diff --git a/acceptance/openstack/sharedfilesystems/v2/services_test.go b/internal/acceptance/openstack/sharedfilesystems/v2/services_test.go similarity index 83% rename from acceptance/openstack/sharedfilesystems/v2/services_test.go rename to internal/acceptance/openstack/sharedfilesystems/v2/services_test.go index b1340e8752..a0745ab795 100644 --- a/acceptance/openstack/sharedfilesystems/v2/services_test.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/services_test.go @@ -6,8 +6,8 @@ package v2 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/services" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/sharedfilesystems/v2/shareaccessrules.go b/internal/acceptance/openstack/sharedfilesystems/v2/shareaccessrules.go similarity index 97% rename from acceptance/openstack/sharedfilesystems/v2/shareaccessrules.go rename to internal/acceptance/openstack/sharedfilesystems/v2/shareaccessrules.go index 2345924e8c..2659cc9834 100644 --- a/acceptance/openstack/sharedfilesystems/v2/shareaccessrules.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/shareaccessrules.go @@ -5,7 +5,7 @@ import ( "testing" "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/shareaccessrules" "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/shares" ) diff --git a/acceptance/openstack/sharedfilesystems/v2/shareaccessrules_test.go b/internal/acceptance/openstack/sharedfilesystems/v2/shareaccessrules_test.go similarity index 96% rename from acceptance/openstack/sharedfilesystems/v2/shareaccessrules_test.go rename to internal/acceptance/openstack/sharedfilesystems/v2/shareaccessrules_test.go index 6670c22ebe..d5a279a923 100644 --- a/acceptance/openstack/sharedfilesystems/v2/shareaccessrules_test.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/shareaccessrules_test.go @@ -6,8 +6,8 @@ package v2 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/sharedfilesystems/v2/sharenetworks.go b/internal/acceptance/openstack/sharedfilesystems/v2/sharenetworks.go similarity index 92% rename from acceptance/openstack/sharedfilesystems/v2/sharenetworks.go rename to internal/acceptance/openstack/sharedfilesystems/v2/sharenetworks.go index 55aad83eee..3a269df8b5 100644 --- a/acceptance/openstack/sharedfilesystems/v2/sharenetworks.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/sharenetworks.go @@ -4,8 +4,8 @@ import ( "testing" "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/sharenetworks" ) diff --git a/acceptance/openstack/sharedfilesystems/v2/sharenetworks_test.go b/internal/acceptance/openstack/sharedfilesystems/v2/sharenetworks_test.go similarity index 98% rename from acceptance/openstack/sharedfilesystems/v2/sharenetworks_test.go rename to internal/acceptance/openstack/sharedfilesystems/v2/sharenetworks_test.go index bcd3f78a89..2b75e52300 100644 --- a/acceptance/openstack/sharedfilesystems/v2/sharenetworks_test.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/sharenetworks_test.go @@ -6,8 +6,8 @@ package v2 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/sharenetworks" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" diff --git a/acceptance/openstack/sharedfilesystems/v2/shares.go b/internal/acceptance/openstack/sharedfilesystems/v2/shares.go similarity index 98% rename from acceptance/openstack/sharedfilesystems/v2/shares.go rename to internal/acceptance/openstack/sharedfilesystems/v2/shares.go index 9179a30cf1..cce4f7feb1 100644 --- a/acceptance/openstack/sharedfilesystems/v2/shares.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/shares.go @@ -5,7 +5,7 @@ import ( "strings" "testing" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/messages" diff --git a/acceptance/openstack/sharedfilesystems/v2/shares_test.go b/internal/acceptance/openstack/sharedfilesystems/v2/shares_test.go similarity index 99% rename from acceptance/openstack/sharedfilesystems/v2/shares_test.go rename to internal/acceptance/openstack/sharedfilesystems/v2/shares_test.go index b1c14d02b0..e05e1b090c 100644 --- a/acceptance/openstack/sharedfilesystems/v2/shares_test.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/shares_test.go @@ -6,8 +6,8 @@ package v2 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/shares" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/sharedfilesystems/v2/sharetransfers.go b/internal/acceptance/openstack/sharedfilesystems/v2/sharetransfers.go similarity index 100% rename from acceptance/openstack/sharedfilesystems/v2/sharetransfers.go rename to internal/acceptance/openstack/sharedfilesystems/v2/sharetransfers.go diff --git a/acceptance/openstack/sharedfilesystems/v2/sharetransfers_test.go b/internal/acceptance/openstack/sharedfilesystems/v2/sharetransfers_test.go similarity index 93% rename from acceptance/openstack/sharedfilesystems/v2/sharetransfers_test.go rename to internal/acceptance/openstack/sharedfilesystems/v2/sharetransfers_test.go index 8e3b70df48..09a188682c 100644 --- a/acceptance/openstack/sharedfilesystems/v2/sharetransfers_test.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/sharetransfers_test.go @@ -6,8 +6,8 @@ package v2 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/sharetransfers" diff --git a/acceptance/openstack/sharedfilesystems/v2/sharetypes.go b/internal/acceptance/openstack/sharedfilesystems/v2/sharetypes.go similarity index 95% rename from acceptance/openstack/sharedfilesystems/v2/sharetypes.go rename to internal/acceptance/openstack/sharedfilesystems/v2/sharetypes.go index 4debc1fd33..791f34a08b 100644 --- a/acceptance/openstack/sharedfilesystems/v2/sharetypes.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/sharetypes.go @@ -4,7 +4,7 @@ import ( "testing" "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/sharetypes" ) diff --git a/acceptance/openstack/sharedfilesystems/v2/sharetypes_test.go b/internal/acceptance/openstack/sharedfilesystems/v2/sharetypes_test.go similarity index 97% rename from acceptance/openstack/sharedfilesystems/v2/sharetypes_test.go rename to internal/acceptance/openstack/sharedfilesystems/v2/sharetypes_test.go index 0fa951d701..bfaa8c2eda 100644 --- a/acceptance/openstack/sharedfilesystems/v2/sharetypes_test.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/sharetypes_test.go @@ -6,8 +6,8 @@ package v2 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/sharetypes" ) diff --git a/acceptance/openstack/sharedfilesystems/v2/snapshots.go b/internal/acceptance/openstack/sharedfilesystems/v2/snapshots.go similarity index 97% rename from acceptance/openstack/sharedfilesystems/v2/snapshots.go rename to internal/acceptance/openstack/sharedfilesystems/v2/snapshots.go index e96d02b9b9..641b6440df 100644 --- a/acceptance/openstack/sharedfilesystems/v2/snapshots.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/snapshots.go @@ -6,7 +6,7 @@ import ( "testing" "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/snapshots" ) diff --git a/acceptance/openstack/sharedfilesystems/v2/snapshots_test.go b/internal/acceptance/openstack/sharedfilesystems/v2/snapshots_test.go similarity index 97% rename from acceptance/openstack/sharedfilesystems/v2/snapshots_test.go rename to internal/acceptance/openstack/sharedfilesystems/v2/snapshots_test.go index de83e41cd1..cb5a7f9b33 100644 --- a/acceptance/openstack/sharedfilesystems/v2/snapshots_test.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/snapshots_test.go @@ -6,8 +6,8 @@ package v2 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/snapshots" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/workflow/v2/crontrigger.go b/internal/acceptance/openstack/workflow/v2/crontrigger.go similarity index 97% rename from acceptance/openstack/workflow/v2/crontrigger.go rename to internal/acceptance/openstack/workflow/v2/crontrigger.go index 20a7fd653e..cdd5045133 100644 --- a/acceptance/openstack/workflow/v2/crontrigger.go +++ b/internal/acceptance/openstack/workflow/v2/crontrigger.go @@ -5,7 +5,7 @@ import ( "time" "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/workflow/v2/crontriggers" "github.com/gophercloud/gophercloud/openstack/workflow/v2/workflows" th "github.com/gophercloud/gophercloud/testhelper" diff --git a/acceptance/openstack/workflow/v2/crontriggers_test.go b/internal/acceptance/openstack/workflow/v2/crontriggers_test.go similarity index 91% rename from acceptance/openstack/workflow/v2/crontriggers_test.go rename to internal/acceptance/openstack/workflow/v2/crontriggers_test.go index 48642cd5f2..97c0703771 100644 --- a/acceptance/openstack/workflow/v2/crontriggers_test.go +++ b/internal/acceptance/openstack/workflow/v2/crontriggers_test.go @@ -4,8 +4,8 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/workflow/v2/crontriggers" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/workflow/v2/execution.go b/internal/acceptance/openstack/workflow/v2/execution.go similarity index 97% rename from acceptance/openstack/workflow/v2/execution.go rename to internal/acceptance/openstack/workflow/v2/execution.go index 6eb6d048da..359275e1da 100644 --- a/acceptance/openstack/workflow/v2/execution.go +++ b/internal/acceptance/openstack/workflow/v2/execution.go @@ -5,7 +5,7 @@ import ( "testing" "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/workflow/v2/executions" "github.com/gophercloud/gophercloud/openstack/workflow/v2/workflows" th "github.com/gophercloud/gophercloud/testhelper" diff --git a/acceptance/openstack/workflow/v2/executions_test.go b/internal/acceptance/openstack/workflow/v2/executions_test.go similarity index 90% rename from acceptance/openstack/workflow/v2/executions_test.go rename to internal/acceptance/openstack/workflow/v2/executions_test.go index 86c0dd858b..098f3dadb2 100644 --- a/acceptance/openstack/workflow/v2/executions_test.go +++ b/internal/acceptance/openstack/workflow/v2/executions_test.go @@ -3,8 +3,8 @@ package v2 import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/workflow/v2/executions" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/workflow/v2/workflow.go b/internal/acceptance/openstack/workflow/v2/workflow.go similarity index 97% rename from acceptance/openstack/workflow/v2/workflow.go rename to internal/acceptance/openstack/workflow/v2/workflow.go index de95d0ca60..b81b69902e 100644 --- a/acceptance/openstack/workflow/v2/workflow.go +++ b/internal/acceptance/openstack/workflow/v2/workflow.go @@ -6,7 +6,7 @@ import ( "testing" "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/workflow/v2/workflows" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/openstack/workflow/v2/workflows_test.go b/internal/acceptance/openstack/workflow/v2/workflows_test.go similarity index 89% rename from acceptance/openstack/workflow/v2/workflows_test.go rename to internal/acceptance/openstack/workflow/v2/workflows_test.go index a5fdde6413..163a52b40b 100644 --- a/acceptance/openstack/workflow/v2/workflows_test.go +++ b/internal/acceptance/openstack/workflow/v2/workflows_test.go @@ -4,8 +4,8 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/workflow/v2/workflows" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/acceptance/tools/pkg.go b/internal/acceptance/tools/pkg.go similarity index 100% rename from acceptance/tools/pkg.go rename to internal/acceptance/tools/pkg.go diff --git a/acceptance/tools/tools.go b/internal/acceptance/tools/tools.go similarity index 100% rename from acceptance/tools/tools.go rename to internal/acceptance/tools/tools.go diff --git a/openstack/clustering/v1/profiletypes/testing/requests_test.go b/openstack/clustering/v1/profiletypes/testing/requests_test.go index c4f379b645..3bac5ab349 100644 --- a/openstack/clustering/v1/profiletypes/testing/requests_test.go +++ b/openstack/clustering/v1/profiletypes/testing/requests_test.go @@ -3,7 +3,7 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/clustering/v1/profiletypes" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" diff --git a/script/acceptancetest b/script/acceptancetest index 09db3f38fa..60627a824f 100755 --- a/script/acceptancetest +++ b/script/acceptancetest @@ -17,7 +17,7 @@ mkdir -p ${LOG_DIR} if [[ -z "${ACCEPTANCE_TESTS_FILTER}" ]]; then ACCEPTANCE_TESTS=($(python <<< "print(' '.join($ACCEPTANCE_TESTS))")) else - ACCEPTANCE_TESTS=$(find acceptance/openstack -name '*_test.go' -exec dirname {} \; | sort -n | uniq | grep -P "$ACCEPTANCE_TESTS_FILTER") + ACCEPTANCE_TESTS=$(find internal/acceptance/openstack -name '*_test.go' -exec dirname {} \; | sort -n | uniq | grep -P "$ACCEPTANCE_TESTS_FILTER") ACCEPTANCE_TESTS=($ACCEPTANCE_TESTS) fi From 39c2b02006ee7b55b81098346cb18f0a235ff10c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= <m.andre@redhat.com> Date: Fri, 22 Sep 2023 16:15:25 +0200 Subject: [PATCH 309/360] Fix acceptancetest script when running without argument It previously generated an error due to the `ACCEPTANCE_TESTS` variable being undefined. It now runs all acceptance tests. --- script/acceptancetest | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/script/acceptancetest b/script/acceptancetest index 60627a824f..9e540b2d84 100755 --- a/script/acceptancetest +++ b/script/acceptancetest @@ -15,11 +15,11 @@ fi mkdir -p ${LOG_DIR} if [[ -z "${ACCEPTANCE_TESTS_FILTER}" ]]; then - ACCEPTANCE_TESTS=($(python <<< "print(' '.join($ACCEPTANCE_TESTS))")) + ACCEPTANCE_TESTS=$(find internal/acceptance/openstack -name '*_test.go' -exec dirname {} \; | sort -n | uniq) else ACCEPTANCE_TESTS=$(find internal/acceptance/openstack -name '*_test.go' -exec dirname {} \; | sort -n | uniq | grep -P "$ACCEPTANCE_TESTS_FILTER") - ACCEPTANCE_TESTS=($ACCEPTANCE_TESTS) fi +ACCEPTANCE_TESTS=($ACCEPTANCE_TESTS) if [[ -z $ACCEPTANCE_TESTS ]]; then echo "No acceptance tests to run" From 4f4a0567959f36d1edc638ce77d275bb0805ba35 Mon Sep 17 00:00:00 2001 From: Emilien Macchi <emacchi@redhat.com> Date: Fri, 22 Sep 2023 15:08:05 -0400 Subject: [PATCH 310/360] Prepare 1.7.0 --- CHANGELOG.md | 16 ++++++++++++++++ provider_client.go | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fb9d4172a8..b470df398b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,19 @@ +## 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: diff --git a/provider_client.go b/provider_client.go index d1955b65ab..e53b713cd4 100644 --- a/provider_client.go +++ b/provider_client.go @@ -14,7 +14,7 @@ import ( // DefaultUserAgent is the default User-Agent string set in the request header. const ( - DefaultUserAgent = "gophercloud/v1.6.0" + DefaultUserAgent = "gophercloud/v1.7.0" DefaultMaxBackoffRetries = 60 ) From 80b7ededfa080eeab26df8e998afe7527b257f40 Mon Sep 17 00:00:00 2001 From: Emilien Macchi <emacchi@redhat.com> Date: Wed, 4 Oct 2023 11:10:56 -0400 Subject: [PATCH 311/360] ci/unit: switch to coverallsapp/github-action Instead of using an old github action for coverall, switch to the official one. --- .github/workflows/unit.yml | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/.github/workflows/unit.yml b/.github/workflows/unit.yml index 9a49c86d26..b438f33a4b 100644 --- a/.github/workflows/unit.yml +++ b/.github/workflows/unit.yml @@ -6,7 +6,7 @@ permissions: jobs: test: permissions: - checks: write # for shogo82148/actions-goveralls to create a new check based on the results + checks: write # for coverallsapp/github-action to create a new check based on the results contents: read # for actions/checkout to fetch code runs-on: ubuntu-latest strategy: @@ -47,18 +47,22 @@ jobs: ./script/format ./script/unittest -v - - uses: shogo82148/actions-goveralls@v1 + - name: Coveralls Parallel + uses: coverallsapp/github-action@v2 with: - path-to-profile: cover.out + file: cover.out flag-name: Go-${{ matrix.go-version }} parallel: true finish: permissions: - checks: write # for shogo82148/actions-goveralls to create a new check based on the results + checks: write # for coverallsapp/github-action to create a new check based on the results needs: test + if: ${{ always() }} runs-on: ubuntu-latest steps: - - uses: shogo82148/actions-goveralls@v1 - with: - parallel-finished: true + - name: Coveralls Finished + uses: coverallsapp/github-action@v2 + with: + parallel-finished: true + carryforward: Go-${{ join(matrix.go-version.*, '-') }} From 964ece518673a3725f2b0fb142dcfacc2146382b Mon Sep 17 00:00:00 2001 From: Dmitry Tantsur <dtantsur@protonmail.com> Date: Fri, 6 Oct 2023 16:27:07 +0200 Subject: [PATCH 312/360] Fix options initialization in ServiceClient.Request (fixes #2798) Request is a part of the public API, but it relies on being called by Get/Post/etc to properly initialize its options. Namely: 1) it may crash on a nil map assignment if there are MoreHeaders, 2) it does not handle microversions. This change moves the relevant code to Request. --- service_client.go | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/service_client.go b/service_client.go index dd54abe30e..94a161e340 100644 --- a/service_client.go +++ b/service_client.go @@ -47,7 +47,7 @@ func (client *ServiceClient) ServiceURL(parts ...string) string { return client.ResourceBaseURL() + strings.Join(parts, "/") } -func (client *ServiceClient) initReqOpts(url string, JSONBody interface{}, JSONResponse interface{}, opts *RequestOpts) { +func (client *ServiceClient) initReqOpts(JSONBody interface{}, JSONResponse interface{}, opts *RequestOpts) { if v, ok := (JSONBody).(io.Reader); ok { opts.RawBody = v } else if JSONBody != nil { @@ -57,14 +57,6 @@ func (client *ServiceClient) initReqOpts(url string, JSONBody interface{}, JSONR if JSONResponse != nil { opts.JSONResponse = JSONResponse } - - if opts.MoreHeaders == nil { - opts.MoreHeaders = make(map[string]string) - } - - if client.Microversion != "" { - client.setMicroversionHeader(opts) - } } // Get calls `Request` with the "GET" HTTP verb. @@ -72,7 +64,7 @@ func (client *ServiceClient) Get(url string, JSONResponse interface{}, opts *Req if opts == nil { opts = new(RequestOpts) } - client.initReqOpts(url, nil, JSONResponse, opts) + client.initReqOpts(nil, JSONResponse, opts) return client.Request("GET", url, opts) } @@ -81,7 +73,7 @@ func (client *ServiceClient) Post(url string, JSONBody interface{}, JSONResponse if opts == nil { opts = new(RequestOpts) } - client.initReqOpts(url, JSONBody, JSONResponse, opts) + client.initReqOpts(JSONBody, JSONResponse, opts) return client.Request("POST", url, opts) } @@ -90,7 +82,7 @@ func (client *ServiceClient) Put(url string, JSONBody interface{}, JSONResponse if opts == nil { opts = new(RequestOpts) } - client.initReqOpts(url, JSONBody, JSONResponse, opts) + client.initReqOpts(JSONBody, JSONResponse, opts) return client.Request("PUT", url, opts) } @@ -99,7 +91,7 @@ func (client *ServiceClient) Patch(url string, JSONBody interface{}, JSONRespons if opts == nil { opts = new(RequestOpts) } - client.initReqOpts(url, JSONBody, JSONResponse, opts) + client.initReqOpts(JSONBody, JSONResponse, opts) return client.Request("PATCH", url, opts) } @@ -108,7 +100,7 @@ func (client *ServiceClient) Delete(url string, opts *RequestOpts) (*http.Respon if opts == nil { opts = new(RequestOpts) } - client.initReqOpts(url, nil, nil, opts) + client.initReqOpts(nil, nil, opts) return client.Request("DELETE", url, opts) } @@ -117,7 +109,7 @@ func (client *ServiceClient) Head(url string, opts *RequestOpts) (*http.Response if opts == nil { opts = new(RequestOpts) } - client.initReqOpts(url, nil, nil, opts) + client.initReqOpts(nil, nil, opts) return client.Request("HEAD", url, opts) } @@ -142,10 +134,19 @@ func (client *ServiceClient) setMicroversionHeader(opts *RequestOpts) { // Request carries out the HTTP operation for the service client func (client *ServiceClient) Request(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 } From 8d0b6da95c0476e3b8f867a0981148f504a7ac6d Mon Sep 17 00:00:00 2001 From: Iury Gregory Melo Ferreira <iurygregory@gmail.com> Date: Fri, 6 Oct 2023 12:12:20 -0400 Subject: [PATCH 313/360] Add job for bobcat stable/2023.2 This commit adds bobcat release to all functional workflows. --- .github/workflows/functional-baremetal.yaml | 3 +++ .github/workflows/functional-basic.yaml | 3 +++ .github/workflows/functional-blockstorage.yaml | 3 +++ .github/workflows/functional-clustering.yaml | 3 +++ .github/workflows/functional-compute.yaml | 3 +++ .github/workflows/functional-containerinfra.yaml | 3 +++ .github/workflows/functional-dns.yaml | 3 +++ .github/workflows/functional-fwaas_v2.yaml | 3 +++ .github/workflows/functional-identity.yaml | 3 +++ .github/workflows/functional-imageservice.yaml | 3 +++ .github/workflows/functional-keymanager.yaml | 3 +++ .github/workflows/functional-loadbalancer.yaml | 3 +++ .github/workflows/functional-messaging.yaml | 3 +++ .github/workflows/functional-networking.yaml | 3 +++ .github/workflows/functional-objectstorage.yaml | 3 +++ .github/workflows/functional-orchestration.yaml | 3 +++ .github/workflows/functional-placement.yaml | 3 +++ .github/workflows/functional-sharedfilesystems.yaml | 3 +++ 18 files changed, 54 insertions(+) diff --git a/.github/workflows/functional-baremetal.yaml b/.github/workflows/functional-baremetal.yaml index 5002c08be0..6a8eb98fcf 100644 --- a/.github/workflows/functional-baremetal.yaml +++ b/.github/workflows/functional-baremetal.yaml @@ -12,6 +12,9 @@ jobs: openstack_version: ["master"] ubuntu_version: ["22.04"] include: + - name: "bobcat" + openstack_version: "stable/2023.2" + ubuntu_version: "22.04" - name: "antelope" openstack_version: "stable/2023.1" ubuntu_version: "22.04" diff --git a/.github/workflows/functional-basic.yaml b/.github/workflows/functional-basic.yaml index b836cfb711..879bd678ad 100644 --- a/.github/workflows/functional-basic.yaml +++ b/.github/workflows/functional-basic.yaml @@ -15,6 +15,9 @@ jobs: openstack_version: ["master"] ubuntu_version: ["22.04"] include: + - name: "bobcat" + openstack_version: "stable/2023.2" + ubuntu_version: "22.04" - name: "antelope" openstack_version: "stable/2023.1" ubuntu_version: "22.04" diff --git a/.github/workflows/functional-blockstorage.yaml b/.github/workflows/functional-blockstorage.yaml index 4b8f69133e..e34f2c3de8 100644 --- a/.github/workflows/functional-blockstorage.yaml +++ b/.github/workflows/functional-blockstorage.yaml @@ -12,6 +12,9 @@ jobs: openstack_version: ["master"] ubuntu_version: ["22.04"] include: + - name: "bobcat" + openstack_version: "stable/2023.2" + ubuntu_version: "22.04" - name: "antelope" openstack_version: "stable/2023.1" ubuntu_version: "22.04" diff --git a/.github/workflows/functional-clustering.yaml b/.github/workflows/functional-clustering.yaml index 3a08f8b1d6..6d86dc6880 100644 --- a/.github/workflows/functional-clustering.yaml +++ b/.github/workflows/functional-clustering.yaml @@ -12,6 +12,9 @@ jobs: openstack_version: ["master"] ubuntu_version: ["22.04"] include: + - name: "bobcat" + openstack_version: "stable/2023.2" + ubuntu_version: "22.04" - name: "antelope" openstack_version: "stable/2023.1" ubuntu_version: "22.04" diff --git a/.github/workflows/functional-compute.yaml b/.github/workflows/functional-compute.yaml index 65311bce6e..cdc789793f 100644 --- a/.github/workflows/functional-compute.yaml +++ b/.github/workflows/functional-compute.yaml @@ -12,6 +12,9 @@ jobs: openstack_version: ["master"] ubuntu_version: ["22.04"] include: + - name: "bobcat" + openstack_version: "stable/2023.2" + ubuntu_version: "22.04" - name: "antelope" openstack_version: "stable/2023.1" ubuntu_version: "22.04" diff --git a/.github/workflows/functional-containerinfra.yaml b/.github/workflows/functional-containerinfra.yaml index cbdbb5cce1..2c7b4a3480 100644 --- a/.github/workflows/functional-containerinfra.yaml +++ b/.github/workflows/functional-containerinfra.yaml @@ -12,6 +12,9 @@ jobs: - name: "master" openstack_version: "master" ubuntu_version: "22.04" + - name: "bobcat" + openstack_version: "stable/2023.2" + ubuntu_version: "22.04" - name: "antelope" openstack_version: "stable/2023.1" ubuntu_version: "22.04" diff --git a/.github/workflows/functional-dns.yaml b/.github/workflows/functional-dns.yaml index e332d273ae..427832a95a 100644 --- a/.github/workflows/functional-dns.yaml +++ b/.github/workflows/functional-dns.yaml @@ -13,6 +13,9 @@ jobs: - name: "master" openstack_version: "master" ubuntu_version: "22.04" + - name: "bobcat" + openstack_version: "stable/2023.2" + ubuntu_version: "22.04" - name: "antelope" openstack_version: "stable/2023.1" ubuntu_version: "22.04" diff --git a/.github/workflows/functional-fwaas_v2.yaml b/.github/workflows/functional-fwaas_v2.yaml index ecd53ef5be..17dd723f29 100644 --- a/.github/workflows/functional-fwaas_v2.yaml +++ b/.github/workflows/functional-fwaas_v2.yaml @@ -12,6 +12,9 @@ jobs: openstack_version: ["master"] ubuntu_version: ["22.04"] include: + - name: "bobcat" + openstack_version: "stable/2023.2" + ubuntu_version: "22.04" - name: "antelope" openstack_version: "stable/2023.1" ubuntu_version: "22.04" diff --git a/.github/workflows/functional-identity.yaml b/.github/workflows/functional-identity.yaml index 3170e41e06..43e638381f 100644 --- a/.github/workflows/functional-identity.yaml +++ b/.github/workflows/functional-identity.yaml @@ -12,6 +12,9 @@ jobs: openstack_version: ["master"] ubuntu_version: ["22.04"] include: + - name: "bobcat" + openstack_version: "stable/2023.2" + ubuntu_version: "22.04" - name: "antelope" openstack_version: "stable/2023.1" ubuntu_version: "22.04" diff --git a/.github/workflows/functional-imageservice.yaml b/.github/workflows/functional-imageservice.yaml index 744ee93a05..a85e6621e2 100644 --- a/.github/workflows/functional-imageservice.yaml +++ b/.github/workflows/functional-imageservice.yaml @@ -12,6 +12,9 @@ jobs: openstack_version: ["master"] ubuntu_version: ["22.04"] include: + - name: "bobcat" + openstack_version: "stable/2023.2" + ubuntu_version: "22.04" - name: "antelope" openstack_version: "stable/2023.1" ubuntu_version: "22.04" diff --git a/.github/workflows/functional-keymanager.yaml b/.github/workflows/functional-keymanager.yaml index 8d72e06a60..a1b5a4ed19 100644 --- a/.github/workflows/functional-keymanager.yaml +++ b/.github/workflows/functional-keymanager.yaml @@ -12,6 +12,9 @@ jobs: openstack_version: ["master"] ubuntu_version: ["22.04"] include: + - name: "bobcat" + openstack_version: "stable/2023.2" + ubuntu_version: "22.04" - name: "antelope" openstack_version: "stable/2023.1" ubuntu_version: "22.04" diff --git a/.github/workflows/functional-loadbalancer.yaml b/.github/workflows/functional-loadbalancer.yaml index 9af476b3a5..fa2a3e9493 100644 --- a/.github/workflows/functional-loadbalancer.yaml +++ b/.github/workflows/functional-loadbalancer.yaml @@ -12,6 +12,9 @@ jobs: openstack_version: ["master"] ubuntu_version: ["22.04"] include: + - name: "bobcat" + openstack_version: "stable/2023.2" + ubuntu_version: "22.04" - name: "antelope" openstack_version: "stable/2023.1" ubuntu_version: "22.04" diff --git a/.github/workflows/functional-messaging.yaml b/.github/workflows/functional-messaging.yaml index 0fcf3c0c35..200ed18887 100644 --- a/.github/workflows/functional-messaging.yaml +++ b/.github/workflows/functional-messaging.yaml @@ -12,6 +12,9 @@ jobs: openstack_version: ["master"] ubuntu_version: ["22.04"] include: + - name: "bobcat" + openstack_version: "stable/2023.2" + ubuntu_version: "22.04" - name: "antelope" openstack_version: "stable/2023.1" ubuntu_version: "22.04" diff --git a/.github/workflows/functional-networking.yaml b/.github/workflows/functional-networking.yaml index 0a33e5787c..4691455e9e 100644 --- a/.github/workflows/functional-networking.yaml +++ b/.github/workflows/functional-networking.yaml @@ -12,6 +12,9 @@ jobs: - name: "master" openstack_version: "master" ubuntu_version: "22.04" + - name: "bobcat" + openstack_version: "stable/2023.2" + ubuntu_version: "22.04" - name: "antelope" openstack_version: "stable/2023.1" ubuntu_version: "22.04" diff --git a/.github/workflows/functional-objectstorage.yaml b/.github/workflows/functional-objectstorage.yaml index bf7e4f5b6f..c5ac5bd4a1 100644 --- a/.github/workflows/functional-objectstorage.yaml +++ b/.github/workflows/functional-objectstorage.yaml @@ -12,6 +12,9 @@ jobs: openstack_version: ["master"] ubuntu_version: ["22.04"] include: + - name: "bobcat" + openstack_version: "stable/2023.2" + ubuntu_version: "22.04" - name: "antelope" openstack_version: "stable/2023.1" ubuntu_version: "22.04" diff --git a/.github/workflows/functional-orchestration.yaml b/.github/workflows/functional-orchestration.yaml index 5f81b3e733..0d035f6be0 100644 --- a/.github/workflows/functional-orchestration.yaml +++ b/.github/workflows/functional-orchestration.yaml @@ -12,6 +12,9 @@ jobs: openstack_version: ["master"] ubuntu_version: ["22.04"] include: + - name: "bobcat" + openstack_version: "stable/2023.2" + ubuntu_version: "22.04" - name: "antelope" openstack_version: "stable/2023.1" ubuntu_version: "22.04" diff --git a/.github/workflows/functional-placement.yaml b/.github/workflows/functional-placement.yaml index a7efecdd24..66d3849a98 100644 --- a/.github/workflows/functional-placement.yaml +++ b/.github/workflows/functional-placement.yaml @@ -12,6 +12,9 @@ jobs: openstack_version: ["master"] ubuntu_version: ["22.04"] include: + - name: "bobcat" + openstack_version: "stable/2023.2" + ubuntu_version: "22.04" - name: "antelope" openstack_version: "stable/2023.1" ubuntu_version: "22.04" diff --git a/.github/workflows/functional-sharedfilesystems.yaml b/.github/workflows/functional-sharedfilesystems.yaml index 48acec333e..eb42329634 100644 --- a/.github/workflows/functional-sharedfilesystems.yaml +++ b/.github/workflows/functional-sharedfilesystems.yaml @@ -12,6 +12,9 @@ jobs: openstack_version: ["master"] ubuntu_version: ["22.04"] include: + - name: "bobcat" + openstack_version: "stable/2023.2" + ubuntu_version: "22.04" - name: "antelope" openstack_version: "stable/2023.1" ubuntu_version: "22.04" From c71fc9d7c2b95b8de308f34f2927203a7eac1e25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= <m.andre@redhat.com> Date: Mon, 16 Oct 2023 14:11:27 +0200 Subject: [PATCH 314/360] Make fixtures part of tests By renaming the fixtures to fixtures_test, we mark them as test, and no longer part of the public API. Some files were left out as they were included in other tests: $ grep "/testing" -R openstack | cut -d " " -f 2 | sort | uniq "github.com/gophercloud/gophercloud/openstack/common/extensions/testing" "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/testing" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/floatingips/testing" "github.com/gophercloud/gophercloud/openstack/networking/v2/networks/testing" "github.com/gophercloud/gophercloud/openstack/networking/v2/ports/testing" "github.com/gophercloud/gophercloud/openstack/objectstorage/v1/accounts/testing" "github.com/gophercloud/gophercloud/openstack/objectstorage/v1/containers/testing" This should prevent go-apidiff from complaining when modifying fixtures. This commit mirrors 7f1d07519ffc99fdc74e1dafc92d56be52624989 that merged in master. --- .../.template/testing/{fixtures.go => fixtures_test.go} | 0 .../apiversions/testing/{fixtures.go => fixtures_test.go} | 0 .../v1/allocations/testing/{fixtures.go => fixtures_test.go} | 0 .../v1/conductors/testing/{fixtures.go => fixtures_test.go} | 0 .../v1/drivers/testing/{fixtures.go => fixtures_test.go} | 0 .../baremetal/v1/nodes/testing/{fixtures.go => fixtures_test.go} | 0 .../baremetal/v1/ports/testing/{fixtures.go => fixtures_test.go} | 0 .../v1/introspection/testing/{fixtures.go => fixtures_test.go} | 0 .../apiversions/testing/{fixtures.go => fixtures_test.go} | 0 .../availabilityzones/testing/{fixtures.go => fixtures_test.go} | 0 .../extensions/backups/testing/{fixtures.go => fixtures_test.go} | 0 .../extensions/limits/testing/{fixtures.go => fixtures_test.go} | 0 .../quotasets/testing/{fixtures.go => fixtures_test.go} | 0 .../schedulerstats/testing/{fixtures.go => fixtures_test.go} | 0 .../extensions/services/testing/{fixtures.go => fixtures_test.go} | 0 .../volumeactions/testing/{fixtures.go => fixtures_test.go} | 0 .../volumetransfers/testing/{fixtures.go => fixtures_test.go} | 0 .../blockstorage/noauth/testing/{fixtures.go => fixtures_test.go} | 0 .../v1/apiversions/testing/{fixtures.go => fixtures_test.go} | 0 .../v1/snapshots/testing/{fixtures.go => fixtures_test.go} | 0 .../v1/volumes/testing/{fixtures.go => fixtures_test.go} | 0 .../v1/volumetypes/testing/{fixtures.go => fixtures_test.go} | 0 .../v2/snapshots/testing/{fixtures.go => fixtures_test.go} | 0 .../v2/volumes/testing/{fixtures.go => fixtures_test.go} | 0 .../v3/attachments/testing/{fixtures.go => fixtures_test.go} | 0 .../blockstorage/v3/qos/testing/{fixtures.go => fixtures_test.go} | 0 .../v3/snapshots/testing/{fixtures.go => fixtures_test.go} | 0 .../v3/volumes/testing/{fixtures.go => fixtures_test.go} | 0 .../v3/volumetypes/testing/{fixtures.go => fixtures_test.go} | 0 openstack/cdn/v1/base/testing/{fixtures.go => fixtures_test.go} | 0 .../cdn/v1/flavors/testing/{fixtures.go => fixtures_test.go} | 0 .../v1/serviceassets/testing/{fixtures.go => fixtures_test.go} | 0 .../cdn/v1/services/testing/{fixtures.go => fixtures_test.go} | 0 .../v1/actions/testing/{fixtures.go => fixtures_test.go} | 0 .../v1/clusters/testing/{fixtures.go => fixtures_test.go} | 0 .../v1/events/testing/{fixtures.go => fixtures_test.go} | 0 .../clustering/v1/nodes/testing/{fixtures.go => fixtures_test.go} | 0 .../v1/policies/testing/{fixtures.go => fixtures_test.go} | 0 .../v1/policytypes/testing/{fixtures.go => fixtures_test.go} | 0 .../v1/profiles/testing/{fixtures.go => fixtures_test.go} | 0 .../v1/profiletypes/testing/{fixtures.go => fixtures_test.go} | 0 .../v1/receivers/testing/{fixtures.go => fixtures_test.go} | 0 .../compute/apiversions/testing/{fixtures.go => fixtures_test.go} | 0 .../aggregates/testing/{fixtures.go => fixtures_test.go} | 0 .../attachinterfaces/testing/{fixtures.go => fixtures_test.go} | 0 .../availabilityzones/testing/{fixtures.go => fixtures_test.go} | 0 .../bootfromvolume/testing/{fixtures.go => fixtures_test.go} | 0 .../defsecrules/testing/{fixtures.go => fixtures_test.go} | 0 .../diagnostics/testing/{fixtures.go => fixtures_test.go} | 0 .../extensions/evacuate/testing/{fixtures.go => fixtures_test.go} | 0 .../testing/{fixtures.go => fixtures_test.go} | 0 .../floatingips/testing/{fixtures.go => fixtures_test.go} | 0 .../hypervisors/testing/{fixtures.go => fixtures_test.go} | 0 .../injectnetworkinfo/testing/{fixtures.go => fixtures_test.go} | 0 .../instanceactions/testing/{fixtures.go => fixtures_test.go} | 0 .../extensions/keypairs/testing/{fixtures.go => fixtures_test.go} | 0 .../extensions/limits/testing/{fixtures.go => fixtures_test.go} | 0 .../lockunlock/testing/{fixtures.go => fixtures_test.go} | 0 .../extensions/migrate/testing/{fixtures.go => fixtures_test.go} | 0 .../extensions/networks/testing/{fixtures.go => fixtures_test.go} | 0 .../pauseunpause/testing/{fixtures.go => fixtures_test.go} | 0 .../quotasets/testing/{fixtures.go => fixtures_test.go} | 0 .../remoteconsoles/testing/{fixtures.go => fixtures_test.go} | 0 .../rescueunrescue/testing/{fixtures.go => fixtures_test.go} | 0 .../resetnetwork/testing/{fixtures.go => fixtures_test.go} | 0 .../resetstate/testing/{fixtures.go => fixtures_test.go} | 0 .../secgroups/testing/{fixtures.go => fixtures_test.go} | 0 .../servergroups/testing/{fixtures.go => fixtures_test.go} | 0 .../serverusage/testing/{fixtures.go => fixtures_test.go} | 0 .../extensions/services/testing/{fixtures.go => fixtures_test.go} | 0 .../shelveunshelve/testing/{fixtures.go => fixtures_test.go} | 0 .../startstop/testing/{fixtures.go => fixtures_test.go} | 0 .../suspendresume/testing/{fixtures.go => fixtures_test.go} | 0 .../v2/extensions/tags/testing/{fixtures.go => fixtures_test.go} | 0 .../tenantnetworks/testing/{fixtures.go => fixtures_test.go} | 0 .../v2/extensions/testing/{fixtures.go => fixtures_test.go} | 0 .../v2/extensions/usage/testing/{fixtures.go => fixtures_test.go} | 0 .../volumeattach/testing/{fixtures.go => fixtures_test.go} | 0 .../compute/v2/flavors/testing/{fixtures.go => fixtures_test.go} | 0 .../compute/v2/servers/testing/{fixtures.go => fixtures_test.go} | 0 .../v1/capsules/testing/{fixtures.go => fixtures_test.go} | 0 .../apiversions/testing/{fixtures.go => fixtures_test.go} | 0 .../v1/certificates/testing/{fixtures.go => fixtures_test.go} | 0 .../v1/clusters/testing/{fixtures.go => fixtures_test.go} | 0 .../v1/clustertemplates/testing/{fixtures.go => fixtures_test.go} | 0 .../v1/nodegroups/testing/{fixtures.go => fixtures_test.go} | 0 .../v1/quotas/testing/{fixtures.go => fixtures_test.go} | 0 .../v1/configurations/testing/{fixtures.go => fixtures_test.go} | 0 .../db/v1/databases/testing/{fixtures.go => fixtures_test.go} | 0 .../db/v1/datastores/testing/{fixtures.go => fixtures_test.go} | 0 openstack/db/v1/flavors/testing/{fixtures.go => fixtures_test.go} | 0 .../db/v1/instances/testing/{fixtures.go => fixtures_test.go} | 0 openstack/db/v1/users/testing/{fixtures.go => fixtures_test.go} | 0 .../dns/v2/recordsets/testing/{fixtures.go => fixtures_test.go} | 0 .../v2/transfer/accept/testing/{fixtures.go => fixtures_test.go} | 0 .../v2/transfer/request/testing/{fixtures.go => fixtures_test.go} | 0 openstack/dns/v2/zones/testing/{fixtures.go => fixtures_test.go} | 0 .../admin/roles/testing/{fixtures.go => fixtures_test.go} | 0 .../v2/extensions/testing/{fixtures.go => fixtures_test.go} | 0 .../identity/v2/tenants/testing/{fixtures.go => fixtures_test.go} | 0 .../identity/v2/tokens/testing/{fixtures.go => fixtures_test.go} | 0 .../identity/v2/users/testing/{fixtures.go => fixtures_test.go} | 0 .../testing/{fixtures.go => fixtures_test.go} | 0 .../identity/v3/catalog/testing/{fixtures.go => fixtures_test.go} | 0 .../v3/credentials/testing/{fixtures.go => fixtures_test.go} | 0 .../identity/v3/domains/testing/{fixtures.go => fixtures_test.go} | 0 .../ec2credentials/testing/{fixtures.go => fixtures_test.go} | 0 .../federation/testing/{fixtures.go => fixtures_test.go} | 0 .../extensions/oauth1/testing/{fixtures.go => fixtures_test.go} | 0 .../extensions/trusts/testing/{fixtures.go => fixtures_test.go} | 0 .../identity/v3/groups/testing/{fixtures.go => fixtures_test.go} | 0 .../identity/v3/limits/testing/{fixtures.go => fixtures_test.go} | 0 .../v3/osinherit/testing/{fixtures.go => fixtures_test.go} | 0 .../v3/policies/testing/{fixtures.go => fixtures_test.go} | 0 .../v3/projects/testing/{fixtures.go => fixtures_test.go} | 0 .../identity/v3/regions/testing/{fixtures.go => fixtures_test.go} | 0 .../v3/registeredlimits/testing/{fixtures.go => fixtures_test.go} | 0 .../identity/v3/roles/testing/{fixtures.go => fixtures_test.go} | 0 .../v3/services/testing/{fixtures.go => fixtures_test.go} | 0 .../identity/v3/users/testing/{fixtures.go => fixtures_test.go} | 0 .../v2/imagedata/testing/{fixtures.go => fixtures_test.go} | 0 .../v2/imageimport/testing/{fixtures.go => fixtures_test.go} | 0 .../v2/images/testing/{fixtures.go => fixtures_test.go} | 0 .../v2/members/testing/{fixtures.go => fixtures_test.go} | 0 .../v2/tasks/testing/{fixtures.go => fixtures_test.go} | 0 .../keymanager/v1/acls/testing/{fixtures.go => fixtures_test.go} | 0 .../v1/containers/testing/{fixtures.go => fixtures_test.go} | 0 .../v1/orders/testing/{fixtures.go => fixtures_test.go} | 0 .../v1/secrets/testing/{fixtures.go => fixtures_test.go} | 0 .../v2/amphorae/testing/{fixtures.go => fixtures_test.go} | 0 .../v2/l7policies/testing/{fixtures.go => fixtures_test.go} | 0 .../v2/listeners/testing/{fixtures.go => fixtures_test.go} | 0 .../v2/loadbalancers/testing/{fixtures.go => fixtures_test.go} | 0 .../v2/monitors/testing/{fixtures.go => fixtures_test.go} | 0 .../v2/pools/testing/{fixtures.go => fixtures_test.go} | 0 .../v2/providers/testing/{fixtures.go => fixtures_test.go} | 0 .../v2/quotas/testing/{fixtures.go => fixtures_test.go} | 0 .../messaging/v2/claims/testing/{fixtures.go => fixtures_test.go} | 0 .../v2/messages/testing/{fixtures.go => fixtures_test.go} | 0 .../messaging/v2/queues/testing/{fixtures.go => fixtures_test.go} | 0 .../extensions/agents/testing/{fixtures.go => fixtures_test.go} | 0 .../attributestags/testing/{fixtures.go => fixtures_test.go} | 0 .../v2/extensions/dns/testing/{fixtures.go => fixtures_test.go} | 0 .../extensions/external/testing/{fixtures.go => fixtures_test.go} | 0 .../addressscopes/testing/{fixtures.go => fixtures_test.go} | 0 .../portforwarding/testing/{fixtures.go => fixtures_test.go} | 0 .../lbaas_v2/l7policies/testing/{fixtures.go => fixtures_test.go} | 0 .../lbaas_v2/listeners/testing/{fixtures.go => fixtures_test.go} | 0 .../loadbalancers/testing/{fixtures.go => fixtures_test.go} | 0 .../lbaas_v2/monitors/testing/{fixtures.go => fixtures_test.go} | 0 .../lbaas_v2/pools/testing/{fixtures.go => fixtures_test.go} | 0 .../v2/extensions/mtu/testing/{fixtures.go => fixtures_test.go} | 0 .../testing/{fixtures.go => fixtures_test.go} | 0 .../portsbinding/testing/{fixtures.go => fixtures_test.go} | 0 .../qos/policies/testing/{fixtures.go => fixtures_test.go} | 0 .../qos/rules/testing/{fixtures.go => fixtures_test.go} | 0 .../qos/ruletypes/testing/{fixtures.go => fixtures_test.go} | 0 .../extensions/quotas/testing/{fixtures.go => fixtures_test.go} | 0 .../rbacpolicies/testing/{fixtures.go => fixtures_test.go} | 0 .../security/groups/testing/{fixtures.go => fixtures_test.go} | 0 .../subnetpools/testing/{fixtures.go => fixtures_test.go} | 0 .../trunk_details/testing/{fixtures.go => fixtures_test.go} | 0 .../extensions/trunks/testing/{fixtures.go => fixtures_test.go} | 0 .../vlantransparent/testing/{fixtures.go => fixtures_test.go} | 0 .../v2/subnets/testing/{fixtures.go => fixtures_test.go} | 0 .../v1/objects/testing/{fixtures.go => fixtures_test.go} | 0 .../v1/swauth/testing/{fixtures.go => fixtures_test.go} | 0 .../v1/buildinfo/testing/{fixtures.go => fixtures_test.go} | 0 .../v1/resourcetypes/testing/{fixtures.go => fixtures_test.go} | 0 .../v1/stackevents/testing/{fixtures.go => fixtures_test.go} | 0 .../v1/stackresources/testing/{fixtures.go => fixtures_test.go} | 0 .../v1/stacks/testing/{fixtures.go => fixtures_test.go} | 0 .../v1/stacktemplates/testing/{fixtures.go => fixtures_test.go} | 0 .../resourceproviders/testing/{fixtures.go => fixtures_test.go} | 0 .../apiversions/testing/{fixtures.go => fixtures_test.go} | 0 .../availabilityzones/testing/{fixtures.go => fixtures_test.go} | 0 .../v2/errors/testing/{fixtures.go => fixtures_test.go} | 0 .../v2/messages/testing/{fixtures.go => fixtures_test.go} | 0 .../v2/replicas/testing/{fixtures.go => fixtures_test.go} | 0 .../v2/schedulerstats/testing/{fixtures.go => fixtures_test.go} | 0 .../v2/securityservices/testing/{fixtures.go => fixtures_test.go} | 0 .../v2/services/testing/{fixtures.go => fixtures_test.go} | 0 .../v2/shareaccessrules/testing/{fixtures.go => fixtures_test.go} | 0 .../v2/sharenetworks/testing/{fixtures.go => fixtures_test.go} | 0 .../v2/shares/testing/{fixtures.go => fixtures_test.go} | 0 .../v2/sharetransfers/testing/{fixtures.go => fixtures_test.go} | 0 .../v2/sharetypes/testing/{fixtures.go => fixtures_test.go} | 0 .../v2/snapshots/testing/{fixtures.go => fixtures_test.go} | 0 188 files changed, 0 insertions(+), 0 deletions(-) rename docs/contributor-tutorial/.template/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/baremetal/apiversions/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/baremetal/v1/allocations/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/baremetal/v1/conductors/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/baremetal/v1/drivers/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/baremetal/v1/nodes/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/baremetal/v1/ports/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/baremetalintrospection/v1/introspection/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/blockstorage/apiversions/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/blockstorage/extensions/availabilityzones/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/blockstorage/extensions/backups/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/blockstorage/extensions/limits/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/blockstorage/extensions/quotasets/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/blockstorage/extensions/schedulerstats/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/blockstorage/extensions/services/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/blockstorage/extensions/volumeactions/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/blockstorage/extensions/volumetransfers/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/blockstorage/noauth/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/blockstorage/v1/apiversions/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/blockstorage/v1/snapshots/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/blockstorage/v1/volumes/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/blockstorage/v1/volumetypes/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/blockstorage/v2/snapshots/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/blockstorage/v2/volumes/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/blockstorage/v3/attachments/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/blockstorage/v3/qos/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/blockstorage/v3/snapshots/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/blockstorage/v3/volumes/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/blockstorage/v3/volumetypes/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/cdn/v1/base/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/cdn/v1/flavors/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/cdn/v1/serviceassets/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/cdn/v1/services/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/clustering/v1/actions/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/clustering/v1/clusters/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/clustering/v1/events/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/clustering/v1/nodes/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/clustering/v1/policies/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/clustering/v1/policytypes/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/clustering/v1/profiles/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/clustering/v1/profiletypes/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/clustering/v1/receivers/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/compute/apiversions/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/compute/v2/extensions/aggregates/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/compute/v2/extensions/attachinterfaces/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/compute/v2/extensions/availabilityzones/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/compute/v2/extensions/bootfromvolume/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/compute/v2/extensions/defsecrules/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/compute/v2/extensions/diagnostics/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/compute/v2/extensions/evacuate/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/compute/v2/extensions/extendedserverattributes/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/compute/v2/extensions/floatingips/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/compute/v2/extensions/hypervisors/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/compute/v2/extensions/injectnetworkinfo/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/compute/v2/extensions/instanceactions/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/compute/v2/extensions/keypairs/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/compute/v2/extensions/limits/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/compute/v2/extensions/lockunlock/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/compute/v2/extensions/migrate/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/compute/v2/extensions/networks/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/compute/v2/extensions/pauseunpause/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/compute/v2/extensions/quotasets/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/compute/v2/extensions/remoteconsoles/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/compute/v2/extensions/rescueunrescue/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/compute/v2/extensions/resetnetwork/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/compute/v2/extensions/resetstate/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/compute/v2/extensions/secgroups/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/compute/v2/extensions/servergroups/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/compute/v2/extensions/serverusage/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/compute/v2/extensions/services/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/compute/v2/extensions/shelveunshelve/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/compute/v2/extensions/startstop/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/compute/v2/extensions/suspendresume/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/compute/v2/extensions/tags/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/compute/v2/extensions/tenantnetworks/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/compute/v2/extensions/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/compute/v2/extensions/usage/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/compute/v2/extensions/volumeattach/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/compute/v2/flavors/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/compute/v2/servers/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/container/v1/capsules/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/containerinfra/apiversions/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/containerinfra/v1/certificates/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/containerinfra/v1/clusters/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/containerinfra/v1/clustertemplates/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/containerinfra/v1/nodegroups/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/containerinfra/v1/quotas/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/db/v1/configurations/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/db/v1/databases/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/db/v1/datastores/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/db/v1/flavors/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/db/v1/instances/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/db/v1/users/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/dns/v2/recordsets/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/dns/v2/transfer/accept/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/dns/v2/transfer/request/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/dns/v2/zones/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/identity/v2/extensions/admin/roles/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/identity/v2/extensions/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/identity/v2/tenants/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/identity/v2/tokens/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/identity/v2/users/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/identity/v3/applicationcredentials/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/identity/v3/catalog/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/identity/v3/credentials/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/identity/v3/domains/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/identity/v3/extensions/ec2credentials/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/identity/v3/extensions/federation/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/identity/v3/extensions/oauth1/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/identity/v3/extensions/trusts/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/identity/v3/groups/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/identity/v3/limits/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/identity/v3/osinherit/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/identity/v3/policies/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/identity/v3/projects/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/identity/v3/regions/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/identity/v3/registeredlimits/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/identity/v3/roles/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/identity/v3/services/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/identity/v3/users/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/imageservice/v2/imagedata/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/imageservice/v2/imageimport/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/imageservice/v2/images/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/imageservice/v2/members/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/imageservice/v2/tasks/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/keymanager/v1/acls/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/keymanager/v1/containers/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/keymanager/v1/orders/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/keymanager/v1/secrets/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/loadbalancer/v2/amphorae/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/loadbalancer/v2/l7policies/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/loadbalancer/v2/listeners/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/loadbalancer/v2/loadbalancers/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/loadbalancer/v2/monitors/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/loadbalancer/v2/pools/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/loadbalancer/v2/providers/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/loadbalancer/v2/quotas/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/messaging/v2/claims/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/messaging/v2/messages/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/messaging/v2/queues/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/networking/v2/extensions/agents/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/networking/v2/extensions/attributestags/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/networking/v2/extensions/dns/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/networking/v2/extensions/external/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/networking/v2/extensions/layer3/addressscopes/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/networking/v2/extensions/layer3/portforwarding/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/networking/v2/extensions/lbaas_v2/l7policies/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/networking/v2/extensions/lbaas_v2/listeners/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/networking/v2/extensions/lbaas_v2/loadbalancers/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/networking/v2/extensions/lbaas_v2/monitors/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/networking/v2/extensions/lbaas_v2/pools/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/networking/v2/extensions/mtu/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/networking/v2/extensions/networkipavailabilities/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/networking/v2/extensions/portsbinding/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/networking/v2/extensions/qos/policies/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/networking/v2/extensions/qos/rules/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/networking/v2/extensions/qos/ruletypes/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/networking/v2/extensions/quotas/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/networking/v2/extensions/rbacpolicies/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/networking/v2/extensions/security/groups/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/networking/v2/extensions/subnetpools/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/networking/v2/extensions/trunk_details/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/networking/v2/extensions/trunks/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/networking/v2/extensions/vlantransparent/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/networking/v2/subnets/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/objectstorage/v1/objects/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/objectstorage/v1/swauth/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/orchestration/v1/buildinfo/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/orchestration/v1/resourcetypes/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/orchestration/v1/stackevents/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/orchestration/v1/stackresources/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/orchestration/v1/stacks/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/orchestration/v1/stacktemplates/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/placement/v1/resourceproviders/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/sharedfilesystems/apiversions/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/sharedfilesystems/v2/availabilityzones/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/sharedfilesystems/v2/errors/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/sharedfilesystems/v2/messages/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/sharedfilesystems/v2/replicas/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/sharedfilesystems/v2/schedulerstats/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/sharedfilesystems/v2/securityservices/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/sharedfilesystems/v2/services/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/sharedfilesystems/v2/shareaccessrules/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/sharedfilesystems/v2/sharenetworks/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/sharedfilesystems/v2/shares/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/sharedfilesystems/v2/sharetransfers/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/sharedfilesystems/v2/sharetypes/testing/{fixtures.go => fixtures_test.go} (100%) rename openstack/sharedfilesystems/v2/snapshots/testing/{fixtures.go => fixtures_test.go} (100%) diff --git a/docs/contributor-tutorial/.template/testing/fixtures.go b/docs/contributor-tutorial/.template/testing/fixtures_test.go similarity index 100% rename from docs/contributor-tutorial/.template/testing/fixtures.go rename to docs/contributor-tutorial/.template/testing/fixtures_test.go diff --git a/openstack/baremetal/apiversions/testing/fixtures.go b/openstack/baremetal/apiversions/testing/fixtures_test.go similarity index 100% rename from openstack/baremetal/apiversions/testing/fixtures.go rename to openstack/baremetal/apiversions/testing/fixtures_test.go diff --git a/openstack/baremetal/v1/allocations/testing/fixtures.go b/openstack/baremetal/v1/allocations/testing/fixtures_test.go similarity index 100% rename from openstack/baremetal/v1/allocations/testing/fixtures.go rename to openstack/baremetal/v1/allocations/testing/fixtures_test.go diff --git a/openstack/baremetal/v1/conductors/testing/fixtures.go b/openstack/baremetal/v1/conductors/testing/fixtures_test.go similarity index 100% rename from openstack/baremetal/v1/conductors/testing/fixtures.go rename to openstack/baremetal/v1/conductors/testing/fixtures_test.go diff --git a/openstack/baremetal/v1/drivers/testing/fixtures.go b/openstack/baremetal/v1/drivers/testing/fixtures_test.go similarity index 100% rename from openstack/baremetal/v1/drivers/testing/fixtures.go rename to openstack/baremetal/v1/drivers/testing/fixtures_test.go diff --git a/openstack/baremetal/v1/nodes/testing/fixtures.go b/openstack/baremetal/v1/nodes/testing/fixtures_test.go similarity index 100% rename from openstack/baremetal/v1/nodes/testing/fixtures.go rename to openstack/baremetal/v1/nodes/testing/fixtures_test.go diff --git a/openstack/baremetal/v1/ports/testing/fixtures.go b/openstack/baremetal/v1/ports/testing/fixtures_test.go similarity index 100% rename from openstack/baremetal/v1/ports/testing/fixtures.go rename to openstack/baremetal/v1/ports/testing/fixtures_test.go diff --git a/openstack/baremetalintrospection/v1/introspection/testing/fixtures.go b/openstack/baremetalintrospection/v1/introspection/testing/fixtures_test.go similarity index 100% rename from openstack/baremetalintrospection/v1/introspection/testing/fixtures.go rename to openstack/baremetalintrospection/v1/introspection/testing/fixtures_test.go diff --git a/openstack/blockstorage/apiversions/testing/fixtures.go b/openstack/blockstorage/apiversions/testing/fixtures_test.go similarity index 100% rename from openstack/blockstorage/apiversions/testing/fixtures.go rename to openstack/blockstorage/apiversions/testing/fixtures_test.go diff --git a/openstack/blockstorage/extensions/availabilityzones/testing/fixtures.go b/openstack/blockstorage/extensions/availabilityzones/testing/fixtures_test.go similarity index 100% rename from openstack/blockstorage/extensions/availabilityzones/testing/fixtures.go rename to openstack/blockstorage/extensions/availabilityzones/testing/fixtures_test.go diff --git a/openstack/blockstorage/extensions/backups/testing/fixtures.go b/openstack/blockstorage/extensions/backups/testing/fixtures_test.go similarity index 100% rename from openstack/blockstorage/extensions/backups/testing/fixtures.go rename to openstack/blockstorage/extensions/backups/testing/fixtures_test.go diff --git a/openstack/blockstorage/extensions/limits/testing/fixtures.go b/openstack/blockstorage/extensions/limits/testing/fixtures_test.go similarity index 100% rename from openstack/blockstorage/extensions/limits/testing/fixtures.go rename to openstack/blockstorage/extensions/limits/testing/fixtures_test.go diff --git a/openstack/blockstorage/extensions/quotasets/testing/fixtures.go b/openstack/blockstorage/extensions/quotasets/testing/fixtures_test.go similarity index 100% rename from openstack/blockstorage/extensions/quotasets/testing/fixtures.go rename to openstack/blockstorage/extensions/quotasets/testing/fixtures_test.go diff --git a/openstack/blockstorage/extensions/schedulerstats/testing/fixtures.go b/openstack/blockstorage/extensions/schedulerstats/testing/fixtures_test.go similarity index 100% rename from openstack/blockstorage/extensions/schedulerstats/testing/fixtures.go rename to openstack/blockstorage/extensions/schedulerstats/testing/fixtures_test.go diff --git a/openstack/blockstorage/extensions/services/testing/fixtures.go b/openstack/blockstorage/extensions/services/testing/fixtures_test.go similarity index 100% rename from openstack/blockstorage/extensions/services/testing/fixtures.go rename to openstack/blockstorage/extensions/services/testing/fixtures_test.go diff --git a/openstack/blockstorage/extensions/volumeactions/testing/fixtures.go b/openstack/blockstorage/extensions/volumeactions/testing/fixtures_test.go similarity index 100% rename from openstack/blockstorage/extensions/volumeactions/testing/fixtures.go rename to openstack/blockstorage/extensions/volumeactions/testing/fixtures_test.go diff --git a/openstack/blockstorage/extensions/volumetransfers/testing/fixtures.go b/openstack/blockstorage/extensions/volumetransfers/testing/fixtures_test.go similarity index 100% rename from openstack/blockstorage/extensions/volumetransfers/testing/fixtures.go rename to openstack/blockstorage/extensions/volumetransfers/testing/fixtures_test.go diff --git a/openstack/blockstorage/noauth/testing/fixtures.go b/openstack/blockstorage/noauth/testing/fixtures_test.go similarity index 100% rename from openstack/blockstorage/noauth/testing/fixtures.go rename to openstack/blockstorage/noauth/testing/fixtures_test.go diff --git a/openstack/blockstorage/v1/apiversions/testing/fixtures.go b/openstack/blockstorage/v1/apiversions/testing/fixtures_test.go similarity index 100% rename from openstack/blockstorage/v1/apiversions/testing/fixtures.go rename to openstack/blockstorage/v1/apiversions/testing/fixtures_test.go diff --git a/openstack/blockstorage/v1/snapshots/testing/fixtures.go b/openstack/blockstorage/v1/snapshots/testing/fixtures_test.go similarity index 100% rename from openstack/blockstorage/v1/snapshots/testing/fixtures.go rename to openstack/blockstorage/v1/snapshots/testing/fixtures_test.go diff --git a/openstack/blockstorage/v1/volumes/testing/fixtures.go b/openstack/blockstorage/v1/volumes/testing/fixtures_test.go similarity index 100% rename from openstack/blockstorage/v1/volumes/testing/fixtures.go rename to openstack/blockstorage/v1/volumes/testing/fixtures_test.go diff --git a/openstack/blockstorage/v1/volumetypes/testing/fixtures.go b/openstack/blockstorage/v1/volumetypes/testing/fixtures_test.go similarity index 100% rename from openstack/blockstorage/v1/volumetypes/testing/fixtures.go rename to openstack/blockstorage/v1/volumetypes/testing/fixtures_test.go diff --git a/openstack/blockstorage/v2/snapshots/testing/fixtures.go b/openstack/blockstorage/v2/snapshots/testing/fixtures_test.go similarity index 100% rename from openstack/blockstorage/v2/snapshots/testing/fixtures.go rename to openstack/blockstorage/v2/snapshots/testing/fixtures_test.go diff --git a/openstack/blockstorage/v2/volumes/testing/fixtures.go b/openstack/blockstorage/v2/volumes/testing/fixtures_test.go similarity index 100% rename from openstack/blockstorage/v2/volumes/testing/fixtures.go rename to openstack/blockstorage/v2/volumes/testing/fixtures_test.go diff --git a/openstack/blockstorage/v3/attachments/testing/fixtures.go b/openstack/blockstorage/v3/attachments/testing/fixtures_test.go similarity index 100% rename from openstack/blockstorage/v3/attachments/testing/fixtures.go rename to openstack/blockstorage/v3/attachments/testing/fixtures_test.go diff --git a/openstack/blockstorage/v3/qos/testing/fixtures.go b/openstack/blockstorage/v3/qos/testing/fixtures_test.go similarity index 100% rename from openstack/blockstorage/v3/qos/testing/fixtures.go rename to openstack/blockstorage/v3/qos/testing/fixtures_test.go diff --git a/openstack/blockstorage/v3/snapshots/testing/fixtures.go b/openstack/blockstorage/v3/snapshots/testing/fixtures_test.go similarity index 100% rename from openstack/blockstorage/v3/snapshots/testing/fixtures.go rename to openstack/blockstorage/v3/snapshots/testing/fixtures_test.go diff --git a/openstack/blockstorage/v3/volumes/testing/fixtures.go b/openstack/blockstorage/v3/volumes/testing/fixtures_test.go similarity index 100% rename from openstack/blockstorage/v3/volumes/testing/fixtures.go rename to openstack/blockstorage/v3/volumes/testing/fixtures_test.go diff --git a/openstack/blockstorage/v3/volumetypes/testing/fixtures.go b/openstack/blockstorage/v3/volumetypes/testing/fixtures_test.go similarity index 100% rename from openstack/blockstorage/v3/volumetypes/testing/fixtures.go rename to openstack/blockstorage/v3/volumetypes/testing/fixtures_test.go diff --git a/openstack/cdn/v1/base/testing/fixtures.go b/openstack/cdn/v1/base/testing/fixtures_test.go similarity index 100% rename from openstack/cdn/v1/base/testing/fixtures.go rename to openstack/cdn/v1/base/testing/fixtures_test.go diff --git a/openstack/cdn/v1/flavors/testing/fixtures.go b/openstack/cdn/v1/flavors/testing/fixtures_test.go similarity index 100% rename from openstack/cdn/v1/flavors/testing/fixtures.go rename to openstack/cdn/v1/flavors/testing/fixtures_test.go diff --git a/openstack/cdn/v1/serviceassets/testing/fixtures.go b/openstack/cdn/v1/serviceassets/testing/fixtures_test.go similarity index 100% rename from openstack/cdn/v1/serviceassets/testing/fixtures.go rename to openstack/cdn/v1/serviceassets/testing/fixtures_test.go diff --git a/openstack/cdn/v1/services/testing/fixtures.go b/openstack/cdn/v1/services/testing/fixtures_test.go similarity index 100% rename from openstack/cdn/v1/services/testing/fixtures.go rename to openstack/cdn/v1/services/testing/fixtures_test.go diff --git a/openstack/clustering/v1/actions/testing/fixtures.go b/openstack/clustering/v1/actions/testing/fixtures_test.go similarity index 100% rename from openstack/clustering/v1/actions/testing/fixtures.go rename to openstack/clustering/v1/actions/testing/fixtures_test.go diff --git a/openstack/clustering/v1/clusters/testing/fixtures.go b/openstack/clustering/v1/clusters/testing/fixtures_test.go similarity index 100% rename from openstack/clustering/v1/clusters/testing/fixtures.go rename to openstack/clustering/v1/clusters/testing/fixtures_test.go diff --git a/openstack/clustering/v1/events/testing/fixtures.go b/openstack/clustering/v1/events/testing/fixtures_test.go similarity index 100% rename from openstack/clustering/v1/events/testing/fixtures.go rename to openstack/clustering/v1/events/testing/fixtures_test.go diff --git a/openstack/clustering/v1/nodes/testing/fixtures.go b/openstack/clustering/v1/nodes/testing/fixtures_test.go similarity index 100% rename from openstack/clustering/v1/nodes/testing/fixtures.go rename to openstack/clustering/v1/nodes/testing/fixtures_test.go diff --git a/openstack/clustering/v1/policies/testing/fixtures.go b/openstack/clustering/v1/policies/testing/fixtures_test.go similarity index 100% rename from openstack/clustering/v1/policies/testing/fixtures.go rename to openstack/clustering/v1/policies/testing/fixtures_test.go diff --git a/openstack/clustering/v1/policytypes/testing/fixtures.go b/openstack/clustering/v1/policytypes/testing/fixtures_test.go similarity index 100% rename from openstack/clustering/v1/policytypes/testing/fixtures.go rename to openstack/clustering/v1/policytypes/testing/fixtures_test.go diff --git a/openstack/clustering/v1/profiles/testing/fixtures.go b/openstack/clustering/v1/profiles/testing/fixtures_test.go similarity index 100% rename from openstack/clustering/v1/profiles/testing/fixtures.go rename to openstack/clustering/v1/profiles/testing/fixtures_test.go diff --git a/openstack/clustering/v1/profiletypes/testing/fixtures.go b/openstack/clustering/v1/profiletypes/testing/fixtures_test.go similarity index 100% rename from openstack/clustering/v1/profiletypes/testing/fixtures.go rename to openstack/clustering/v1/profiletypes/testing/fixtures_test.go diff --git a/openstack/clustering/v1/receivers/testing/fixtures.go b/openstack/clustering/v1/receivers/testing/fixtures_test.go similarity index 100% rename from openstack/clustering/v1/receivers/testing/fixtures.go rename to openstack/clustering/v1/receivers/testing/fixtures_test.go diff --git a/openstack/compute/apiversions/testing/fixtures.go b/openstack/compute/apiversions/testing/fixtures_test.go similarity index 100% rename from openstack/compute/apiversions/testing/fixtures.go rename to openstack/compute/apiversions/testing/fixtures_test.go diff --git a/openstack/compute/v2/extensions/aggregates/testing/fixtures.go b/openstack/compute/v2/extensions/aggregates/testing/fixtures_test.go similarity index 100% rename from openstack/compute/v2/extensions/aggregates/testing/fixtures.go rename to openstack/compute/v2/extensions/aggregates/testing/fixtures_test.go diff --git a/openstack/compute/v2/extensions/attachinterfaces/testing/fixtures.go b/openstack/compute/v2/extensions/attachinterfaces/testing/fixtures_test.go similarity index 100% rename from openstack/compute/v2/extensions/attachinterfaces/testing/fixtures.go rename to openstack/compute/v2/extensions/attachinterfaces/testing/fixtures_test.go diff --git a/openstack/compute/v2/extensions/availabilityzones/testing/fixtures.go b/openstack/compute/v2/extensions/availabilityzones/testing/fixtures_test.go similarity index 100% rename from openstack/compute/v2/extensions/availabilityzones/testing/fixtures.go rename to openstack/compute/v2/extensions/availabilityzones/testing/fixtures_test.go diff --git a/openstack/compute/v2/extensions/bootfromvolume/testing/fixtures.go b/openstack/compute/v2/extensions/bootfromvolume/testing/fixtures_test.go similarity index 100% rename from openstack/compute/v2/extensions/bootfromvolume/testing/fixtures.go rename to openstack/compute/v2/extensions/bootfromvolume/testing/fixtures_test.go diff --git a/openstack/compute/v2/extensions/defsecrules/testing/fixtures.go b/openstack/compute/v2/extensions/defsecrules/testing/fixtures_test.go similarity index 100% rename from openstack/compute/v2/extensions/defsecrules/testing/fixtures.go rename to openstack/compute/v2/extensions/defsecrules/testing/fixtures_test.go diff --git a/openstack/compute/v2/extensions/diagnostics/testing/fixtures.go b/openstack/compute/v2/extensions/diagnostics/testing/fixtures_test.go similarity index 100% rename from openstack/compute/v2/extensions/diagnostics/testing/fixtures.go rename to openstack/compute/v2/extensions/diagnostics/testing/fixtures_test.go diff --git a/openstack/compute/v2/extensions/evacuate/testing/fixtures.go b/openstack/compute/v2/extensions/evacuate/testing/fixtures_test.go similarity index 100% rename from openstack/compute/v2/extensions/evacuate/testing/fixtures.go rename to openstack/compute/v2/extensions/evacuate/testing/fixtures_test.go diff --git a/openstack/compute/v2/extensions/extendedserverattributes/testing/fixtures.go b/openstack/compute/v2/extensions/extendedserverattributes/testing/fixtures_test.go similarity index 100% rename from openstack/compute/v2/extensions/extendedserverattributes/testing/fixtures.go rename to openstack/compute/v2/extensions/extendedserverattributes/testing/fixtures_test.go diff --git a/openstack/compute/v2/extensions/floatingips/testing/fixtures.go b/openstack/compute/v2/extensions/floatingips/testing/fixtures_test.go similarity index 100% rename from openstack/compute/v2/extensions/floatingips/testing/fixtures.go rename to openstack/compute/v2/extensions/floatingips/testing/fixtures_test.go diff --git a/openstack/compute/v2/extensions/hypervisors/testing/fixtures.go b/openstack/compute/v2/extensions/hypervisors/testing/fixtures_test.go similarity index 100% rename from openstack/compute/v2/extensions/hypervisors/testing/fixtures.go rename to openstack/compute/v2/extensions/hypervisors/testing/fixtures_test.go diff --git a/openstack/compute/v2/extensions/injectnetworkinfo/testing/fixtures.go b/openstack/compute/v2/extensions/injectnetworkinfo/testing/fixtures_test.go similarity index 100% rename from openstack/compute/v2/extensions/injectnetworkinfo/testing/fixtures.go rename to openstack/compute/v2/extensions/injectnetworkinfo/testing/fixtures_test.go diff --git a/openstack/compute/v2/extensions/instanceactions/testing/fixtures.go b/openstack/compute/v2/extensions/instanceactions/testing/fixtures_test.go similarity index 100% rename from openstack/compute/v2/extensions/instanceactions/testing/fixtures.go rename to openstack/compute/v2/extensions/instanceactions/testing/fixtures_test.go diff --git a/openstack/compute/v2/extensions/keypairs/testing/fixtures.go b/openstack/compute/v2/extensions/keypairs/testing/fixtures_test.go similarity index 100% rename from openstack/compute/v2/extensions/keypairs/testing/fixtures.go rename to openstack/compute/v2/extensions/keypairs/testing/fixtures_test.go diff --git a/openstack/compute/v2/extensions/limits/testing/fixtures.go b/openstack/compute/v2/extensions/limits/testing/fixtures_test.go similarity index 100% rename from openstack/compute/v2/extensions/limits/testing/fixtures.go rename to openstack/compute/v2/extensions/limits/testing/fixtures_test.go diff --git a/openstack/compute/v2/extensions/lockunlock/testing/fixtures.go b/openstack/compute/v2/extensions/lockunlock/testing/fixtures_test.go similarity index 100% rename from openstack/compute/v2/extensions/lockunlock/testing/fixtures.go rename to openstack/compute/v2/extensions/lockunlock/testing/fixtures_test.go diff --git a/openstack/compute/v2/extensions/migrate/testing/fixtures.go b/openstack/compute/v2/extensions/migrate/testing/fixtures_test.go similarity index 100% rename from openstack/compute/v2/extensions/migrate/testing/fixtures.go rename to openstack/compute/v2/extensions/migrate/testing/fixtures_test.go diff --git a/openstack/compute/v2/extensions/networks/testing/fixtures.go b/openstack/compute/v2/extensions/networks/testing/fixtures_test.go similarity index 100% rename from openstack/compute/v2/extensions/networks/testing/fixtures.go rename to openstack/compute/v2/extensions/networks/testing/fixtures_test.go diff --git a/openstack/compute/v2/extensions/pauseunpause/testing/fixtures.go b/openstack/compute/v2/extensions/pauseunpause/testing/fixtures_test.go similarity index 100% rename from openstack/compute/v2/extensions/pauseunpause/testing/fixtures.go rename to openstack/compute/v2/extensions/pauseunpause/testing/fixtures_test.go diff --git a/openstack/compute/v2/extensions/quotasets/testing/fixtures.go b/openstack/compute/v2/extensions/quotasets/testing/fixtures_test.go similarity index 100% rename from openstack/compute/v2/extensions/quotasets/testing/fixtures.go rename to openstack/compute/v2/extensions/quotasets/testing/fixtures_test.go diff --git a/openstack/compute/v2/extensions/remoteconsoles/testing/fixtures.go b/openstack/compute/v2/extensions/remoteconsoles/testing/fixtures_test.go similarity index 100% rename from openstack/compute/v2/extensions/remoteconsoles/testing/fixtures.go rename to openstack/compute/v2/extensions/remoteconsoles/testing/fixtures_test.go diff --git a/openstack/compute/v2/extensions/rescueunrescue/testing/fixtures.go b/openstack/compute/v2/extensions/rescueunrescue/testing/fixtures_test.go similarity index 100% rename from openstack/compute/v2/extensions/rescueunrescue/testing/fixtures.go rename to openstack/compute/v2/extensions/rescueunrescue/testing/fixtures_test.go diff --git a/openstack/compute/v2/extensions/resetnetwork/testing/fixtures.go b/openstack/compute/v2/extensions/resetnetwork/testing/fixtures_test.go similarity index 100% rename from openstack/compute/v2/extensions/resetnetwork/testing/fixtures.go rename to openstack/compute/v2/extensions/resetnetwork/testing/fixtures_test.go diff --git a/openstack/compute/v2/extensions/resetstate/testing/fixtures.go b/openstack/compute/v2/extensions/resetstate/testing/fixtures_test.go similarity index 100% rename from openstack/compute/v2/extensions/resetstate/testing/fixtures.go rename to openstack/compute/v2/extensions/resetstate/testing/fixtures_test.go diff --git a/openstack/compute/v2/extensions/secgroups/testing/fixtures.go b/openstack/compute/v2/extensions/secgroups/testing/fixtures_test.go similarity index 100% rename from openstack/compute/v2/extensions/secgroups/testing/fixtures.go rename to openstack/compute/v2/extensions/secgroups/testing/fixtures_test.go diff --git a/openstack/compute/v2/extensions/servergroups/testing/fixtures.go b/openstack/compute/v2/extensions/servergroups/testing/fixtures_test.go similarity index 100% rename from openstack/compute/v2/extensions/servergroups/testing/fixtures.go rename to openstack/compute/v2/extensions/servergroups/testing/fixtures_test.go diff --git a/openstack/compute/v2/extensions/serverusage/testing/fixtures.go b/openstack/compute/v2/extensions/serverusage/testing/fixtures_test.go similarity index 100% rename from openstack/compute/v2/extensions/serverusage/testing/fixtures.go rename to openstack/compute/v2/extensions/serverusage/testing/fixtures_test.go diff --git a/openstack/compute/v2/extensions/services/testing/fixtures.go b/openstack/compute/v2/extensions/services/testing/fixtures_test.go similarity index 100% rename from openstack/compute/v2/extensions/services/testing/fixtures.go rename to openstack/compute/v2/extensions/services/testing/fixtures_test.go diff --git a/openstack/compute/v2/extensions/shelveunshelve/testing/fixtures.go b/openstack/compute/v2/extensions/shelveunshelve/testing/fixtures_test.go similarity index 100% rename from openstack/compute/v2/extensions/shelveunshelve/testing/fixtures.go rename to openstack/compute/v2/extensions/shelveunshelve/testing/fixtures_test.go diff --git a/openstack/compute/v2/extensions/startstop/testing/fixtures.go b/openstack/compute/v2/extensions/startstop/testing/fixtures_test.go similarity index 100% rename from openstack/compute/v2/extensions/startstop/testing/fixtures.go rename to openstack/compute/v2/extensions/startstop/testing/fixtures_test.go diff --git a/openstack/compute/v2/extensions/suspendresume/testing/fixtures.go b/openstack/compute/v2/extensions/suspendresume/testing/fixtures_test.go similarity index 100% rename from openstack/compute/v2/extensions/suspendresume/testing/fixtures.go rename to openstack/compute/v2/extensions/suspendresume/testing/fixtures_test.go diff --git a/openstack/compute/v2/extensions/tags/testing/fixtures.go b/openstack/compute/v2/extensions/tags/testing/fixtures_test.go similarity index 100% rename from openstack/compute/v2/extensions/tags/testing/fixtures.go rename to openstack/compute/v2/extensions/tags/testing/fixtures_test.go diff --git a/openstack/compute/v2/extensions/tenantnetworks/testing/fixtures.go b/openstack/compute/v2/extensions/tenantnetworks/testing/fixtures_test.go similarity index 100% rename from openstack/compute/v2/extensions/tenantnetworks/testing/fixtures.go rename to openstack/compute/v2/extensions/tenantnetworks/testing/fixtures_test.go diff --git a/openstack/compute/v2/extensions/testing/fixtures.go b/openstack/compute/v2/extensions/testing/fixtures_test.go similarity index 100% rename from openstack/compute/v2/extensions/testing/fixtures.go rename to openstack/compute/v2/extensions/testing/fixtures_test.go diff --git a/openstack/compute/v2/extensions/usage/testing/fixtures.go b/openstack/compute/v2/extensions/usage/testing/fixtures_test.go similarity index 100% rename from openstack/compute/v2/extensions/usage/testing/fixtures.go rename to openstack/compute/v2/extensions/usage/testing/fixtures_test.go diff --git a/openstack/compute/v2/extensions/volumeattach/testing/fixtures.go b/openstack/compute/v2/extensions/volumeattach/testing/fixtures_test.go similarity index 100% rename from openstack/compute/v2/extensions/volumeattach/testing/fixtures.go rename to openstack/compute/v2/extensions/volumeattach/testing/fixtures_test.go diff --git a/openstack/compute/v2/flavors/testing/fixtures.go b/openstack/compute/v2/flavors/testing/fixtures_test.go similarity index 100% rename from openstack/compute/v2/flavors/testing/fixtures.go rename to openstack/compute/v2/flavors/testing/fixtures_test.go diff --git a/openstack/compute/v2/servers/testing/fixtures.go b/openstack/compute/v2/servers/testing/fixtures_test.go similarity index 100% rename from openstack/compute/v2/servers/testing/fixtures.go rename to openstack/compute/v2/servers/testing/fixtures_test.go diff --git a/openstack/container/v1/capsules/testing/fixtures.go b/openstack/container/v1/capsules/testing/fixtures_test.go similarity index 100% rename from openstack/container/v1/capsules/testing/fixtures.go rename to openstack/container/v1/capsules/testing/fixtures_test.go diff --git a/openstack/containerinfra/apiversions/testing/fixtures.go b/openstack/containerinfra/apiversions/testing/fixtures_test.go similarity index 100% rename from openstack/containerinfra/apiversions/testing/fixtures.go rename to openstack/containerinfra/apiversions/testing/fixtures_test.go diff --git a/openstack/containerinfra/v1/certificates/testing/fixtures.go b/openstack/containerinfra/v1/certificates/testing/fixtures_test.go similarity index 100% rename from openstack/containerinfra/v1/certificates/testing/fixtures.go rename to openstack/containerinfra/v1/certificates/testing/fixtures_test.go diff --git a/openstack/containerinfra/v1/clusters/testing/fixtures.go b/openstack/containerinfra/v1/clusters/testing/fixtures_test.go similarity index 100% rename from openstack/containerinfra/v1/clusters/testing/fixtures.go rename to openstack/containerinfra/v1/clusters/testing/fixtures_test.go diff --git a/openstack/containerinfra/v1/clustertemplates/testing/fixtures.go b/openstack/containerinfra/v1/clustertemplates/testing/fixtures_test.go similarity index 100% rename from openstack/containerinfra/v1/clustertemplates/testing/fixtures.go rename to openstack/containerinfra/v1/clustertemplates/testing/fixtures_test.go diff --git a/openstack/containerinfra/v1/nodegroups/testing/fixtures.go b/openstack/containerinfra/v1/nodegroups/testing/fixtures_test.go similarity index 100% rename from openstack/containerinfra/v1/nodegroups/testing/fixtures.go rename to openstack/containerinfra/v1/nodegroups/testing/fixtures_test.go diff --git a/openstack/containerinfra/v1/quotas/testing/fixtures.go b/openstack/containerinfra/v1/quotas/testing/fixtures_test.go similarity index 100% rename from openstack/containerinfra/v1/quotas/testing/fixtures.go rename to openstack/containerinfra/v1/quotas/testing/fixtures_test.go diff --git a/openstack/db/v1/configurations/testing/fixtures.go b/openstack/db/v1/configurations/testing/fixtures_test.go similarity index 100% rename from openstack/db/v1/configurations/testing/fixtures.go rename to openstack/db/v1/configurations/testing/fixtures_test.go diff --git a/openstack/db/v1/databases/testing/fixtures.go b/openstack/db/v1/databases/testing/fixtures_test.go similarity index 100% rename from openstack/db/v1/databases/testing/fixtures.go rename to openstack/db/v1/databases/testing/fixtures_test.go diff --git a/openstack/db/v1/datastores/testing/fixtures.go b/openstack/db/v1/datastores/testing/fixtures_test.go similarity index 100% rename from openstack/db/v1/datastores/testing/fixtures.go rename to openstack/db/v1/datastores/testing/fixtures_test.go diff --git a/openstack/db/v1/flavors/testing/fixtures.go b/openstack/db/v1/flavors/testing/fixtures_test.go similarity index 100% rename from openstack/db/v1/flavors/testing/fixtures.go rename to openstack/db/v1/flavors/testing/fixtures_test.go diff --git a/openstack/db/v1/instances/testing/fixtures.go b/openstack/db/v1/instances/testing/fixtures_test.go similarity index 100% rename from openstack/db/v1/instances/testing/fixtures.go rename to openstack/db/v1/instances/testing/fixtures_test.go diff --git a/openstack/db/v1/users/testing/fixtures.go b/openstack/db/v1/users/testing/fixtures_test.go similarity index 100% rename from openstack/db/v1/users/testing/fixtures.go rename to openstack/db/v1/users/testing/fixtures_test.go diff --git a/openstack/dns/v2/recordsets/testing/fixtures.go b/openstack/dns/v2/recordsets/testing/fixtures_test.go similarity index 100% rename from openstack/dns/v2/recordsets/testing/fixtures.go rename to openstack/dns/v2/recordsets/testing/fixtures_test.go diff --git a/openstack/dns/v2/transfer/accept/testing/fixtures.go b/openstack/dns/v2/transfer/accept/testing/fixtures_test.go similarity index 100% rename from openstack/dns/v2/transfer/accept/testing/fixtures.go rename to openstack/dns/v2/transfer/accept/testing/fixtures_test.go diff --git a/openstack/dns/v2/transfer/request/testing/fixtures.go b/openstack/dns/v2/transfer/request/testing/fixtures_test.go similarity index 100% rename from openstack/dns/v2/transfer/request/testing/fixtures.go rename to openstack/dns/v2/transfer/request/testing/fixtures_test.go diff --git a/openstack/dns/v2/zones/testing/fixtures.go b/openstack/dns/v2/zones/testing/fixtures_test.go similarity index 100% rename from openstack/dns/v2/zones/testing/fixtures.go rename to openstack/dns/v2/zones/testing/fixtures_test.go diff --git a/openstack/identity/v2/extensions/admin/roles/testing/fixtures.go b/openstack/identity/v2/extensions/admin/roles/testing/fixtures_test.go similarity index 100% rename from openstack/identity/v2/extensions/admin/roles/testing/fixtures.go rename to openstack/identity/v2/extensions/admin/roles/testing/fixtures_test.go diff --git a/openstack/identity/v2/extensions/testing/fixtures.go b/openstack/identity/v2/extensions/testing/fixtures_test.go similarity index 100% rename from openstack/identity/v2/extensions/testing/fixtures.go rename to openstack/identity/v2/extensions/testing/fixtures_test.go diff --git a/openstack/identity/v2/tenants/testing/fixtures.go b/openstack/identity/v2/tenants/testing/fixtures_test.go similarity index 100% rename from openstack/identity/v2/tenants/testing/fixtures.go rename to openstack/identity/v2/tenants/testing/fixtures_test.go diff --git a/openstack/identity/v2/tokens/testing/fixtures.go b/openstack/identity/v2/tokens/testing/fixtures_test.go similarity index 100% rename from openstack/identity/v2/tokens/testing/fixtures.go rename to openstack/identity/v2/tokens/testing/fixtures_test.go diff --git a/openstack/identity/v2/users/testing/fixtures.go b/openstack/identity/v2/users/testing/fixtures_test.go similarity index 100% rename from openstack/identity/v2/users/testing/fixtures.go rename to openstack/identity/v2/users/testing/fixtures_test.go diff --git a/openstack/identity/v3/applicationcredentials/testing/fixtures.go b/openstack/identity/v3/applicationcredentials/testing/fixtures_test.go similarity index 100% rename from openstack/identity/v3/applicationcredentials/testing/fixtures.go rename to openstack/identity/v3/applicationcredentials/testing/fixtures_test.go diff --git a/openstack/identity/v3/catalog/testing/fixtures.go b/openstack/identity/v3/catalog/testing/fixtures_test.go similarity index 100% rename from openstack/identity/v3/catalog/testing/fixtures.go rename to openstack/identity/v3/catalog/testing/fixtures_test.go diff --git a/openstack/identity/v3/credentials/testing/fixtures.go b/openstack/identity/v3/credentials/testing/fixtures_test.go similarity index 100% rename from openstack/identity/v3/credentials/testing/fixtures.go rename to openstack/identity/v3/credentials/testing/fixtures_test.go diff --git a/openstack/identity/v3/domains/testing/fixtures.go b/openstack/identity/v3/domains/testing/fixtures_test.go similarity index 100% rename from openstack/identity/v3/domains/testing/fixtures.go rename to openstack/identity/v3/domains/testing/fixtures_test.go diff --git a/openstack/identity/v3/extensions/ec2credentials/testing/fixtures.go b/openstack/identity/v3/extensions/ec2credentials/testing/fixtures_test.go similarity index 100% rename from openstack/identity/v3/extensions/ec2credentials/testing/fixtures.go rename to openstack/identity/v3/extensions/ec2credentials/testing/fixtures_test.go diff --git a/openstack/identity/v3/extensions/federation/testing/fixtures.go b/openstack/identity/v3/extensions/federation/testing/fixtures_test.go similarity index 100% rename from openstack/identity/v3/extensions/federation/testing/fixtures.go rename to openstack/identity/v3/extensions/federation/testing/fixtures_test.go diff --git a/openstack/identity/v3/extensions/oauth1/testing/fixtures.go b/openstack/identity/v3/extensions/oauth1/testing/fixtures_test.go similarity index 100% rename from openstack/identity/v3/extensions/oauth1/testing/fixtures.go rename to openstack/identity/v3/extensions/oauth1/testing/fixtures_test.go diff --git a/openstack/identity/v3/extensions/trusts/testing/fixtures.go b/openstack/identity/v3/extensions/trusts/testing/fixtures_test.go similarity index 100% rename from openstack/identity/v3/extensions/trusts/testing/fixtures.go rename to openstack/identity/v3/extensions/trusts/testing/fixtures_test.go diff --git a/openstack/identity/v3/groups/testing/fixtures.go b/openstack/identity/v3/groups/testing/fixtures_test.go similarity index 100% rename from openstack/identity/v3/groups/testing/fixtures.go rename to openstack/identity/v3/groups/testing/fixtures_test.go diff --git a/openstack/identity/v3/limits/testing/fixtures.go b/openstack/identity/v3/limits/testing/fixtures_test.go similarity index 100% rename from openstack/identity/v3/limits/testing/fixtures.go rename to openstack/identity/v3/limits/testing/fixtures_test.go diff --git a/openstack/identity/v3/osinherit/testing/fixtures.go b/openstack/identity/v3/osinherit/testing/fixtures_test.go similarity index 100% rename from openstack/identity/v3/osinherit/testing/fixtures.go rename to openstack/identity/v3/osinherit/testing/fixtures_test.go diff --git a/openstack/identity/v3/policies/testing/fixtures.go b/openstack/identity/v3/policies/testing/fixtures_test.go similarity index 100% rename from openstack/identity/v3/policies/testing/fixtures.go rename to openstack/identity/v3/policies/testing/fixtures_test.go diff --git a/openstack/identity/v3/projects/testing/fixtures.go b/openstack/identity/v3/projects/testing/fixtures_test.go similarity index 100% rename from openstack/identity/v3/projects/testing/fixtures.go rename to openstack/identity/v3/projects/testing/fixtures_test.go diff --git a/openstack/identity/v3/regions/testing/fixtures.go b/openstack/identity/v3/regions/testing/fixtures_test.go similarity index 100% rename from openstack/identity/v3/regions/testing/fixtures.go rename to openstack/identity/v3/regions/testing/fixtures_test.go diff --git a/openstack/identity/v3/registeredlimits/testing/fixtures.go b/openstack/identity/v3/registeredlimits/testing/fixtures_test.go similarity index 100% rename from openstack/identity/v3/registeredlimits/testing/fixtures.go rename to openstack/identity/v3/registeredlimits/testing/fixtures_test.go diff --git a/openstack/identity/v3/roles/testing/fixtures.go b/openstack/identity/v3/roles/testing/fixtures_test.go similarity index 100% rename from openstack/identity/v3/roles/testing/fixtures.go rename to openstack/identity/v3/roles/testing/fixtures_test.go diff --git a/openstack/identity/v3/services/testing/fixtures.go b/openstack/identity/v3/services/testing/fixtures_test.go similarity index 100% rename from openstack/identity/v3/services/testing/fixtures.go rename to openstack/identity/v3/services/testing/fixtures_test.go diff --git a/openstack/identity/v3/users/testing/fixtures.go b/openstack/identity/v3/users/testing/fixtures_test.go similarity index 100% rename from openstack/identity/v3/users/testing/fixtures.go rename to openstack/identity/v3/users/testing/fixtures_test.go diff --git a/openstack/imageservice/v2/imagedata/testing/fixtures.go b/openstack/imageservice/v2/imagedata/testing/fixtures_test.go similarity index 100% rename from openstack/imageservice/v2/imagedata/testing/fixtures.go rename to openstack/imageservice/v2/imagedata/testing/fixtures_test.go diff --git a/openstack/imageservice/v2/imageimport/testing/fixtures.go b/openstack/imageservice/v2/imageimport/testing/fixtures_test.go similarity index 100% rename from openstack/imageservice/v2/imageimport/testing/fixtures.go rename to openstack/imageservice/v2/imageimport/testing/fixtures_test.go diff --git a/openstack/imageservice/v2/images/testing/fixtures.go b/openstack/imageservice/v2/images/testing/fixtures_test.go similarity index 100% rename from openstack/imageservice/v2/images/testing/fixtures.go rename to openstack/imageservice/v2/images/testing/fixtures_test.go diff --git a/openstack/imageservice/v2/members/testing/fixtures.go b/openstack/imageservice/v2/members/testing/fixtures_test.go similarity index 100% rename from openstack/imageservice/v2/members/testing/fixtures.go rename to openstack/imageservice/v2/members/testing/fixtures_test.go diff --git a/openstack/imageservice/v2/tasks/testing/fixtures.go b/openstack/imageservice/v2/tasks/testing/fixtures_test.go similarity index 100% rename from openstack/imageservice/v2/tasks/testing/fixtures.go rename to openstack/imageservice/v2/tasks/testing/fixtures_test.go diff --git a/openstack/keymanager/v1/acls/testing/fixtures.go b/openstack/keymanager/v1/acls/testing/fixtures_test.go similarity index 100% rename from openstack/keymanager/v1/acls/testing/fixtures.go rename to openstack/keymanager/v1/acls/testing/fixtures_test.go diff --git a/openstack/keymanager/v1/containers/testing/fixtures.go b/openstack/keymanager/v1/containers/testing/fixtures_test.go similarity index 100% rename from openstack/keymanager/v1/containers/testing/fixtures.go rename to openstack/keymanager/v1/containers/testing/fixtures_test.go diff --git a/openstack/keymanager/v1/orders/testing/fixtures.go b/openstack/keymanager/v1/orders/testing/fixtures_test.go similarity index 100% rename from openstack/keymanager/v1/orders/testing/fixtures.go rename to openstack/keymanager/v1/orders/testing/fixtures_test.go diff --git a/openstack/keymanager/v1/secrets/testing/fixtures.go b/openstack/keymanager/v1/secrets/testing/fixtures_test.go similarity index 100% rename from openstack/keymanager/v1/secrets/testing/fixtures.go rename to openstack/keymanager/v1/secrets/testing/fixtures_test.go diff --git a/openstack/loadbalancer/v2/amphorae/testing/fixtures.go b/openstack/loadbalancer/v2/amphorae/testing/fixtures_test.go similarity index 100% rename from openstack/loadbalancer/v2/amphorae/testing/fixtures.go rename to openstack/loadbalancer/v2/amphorae/testing/fixtures_test.go diff --git a/openstack/loadbalancer/v2/l7policies/testing/fixtures.go b/openstack/loadbalancer/v2/l7policies/testing/fixtures_test.go similarity index 100% rename from openstack/loadbalancer/v2/l7policies/testing/fixtures.go rename to openstack/loadbalancer/v2/l7policies/testing/fixtures_test.go diff --git a/openstack/loadbalancer/v2/listeners/testing/fixtures.go b/openstack/loadbalancer/v2/listeners/testing/fixtures_test.go similarity index 100% rename from openstack/loadbalancer/v2/listeners/testing/fixtures.go rename to openstack/loadbalancer/v2/listeners/testing/fixtures_test.go diff --git a/openstack/loadbalancer/v2/loadbalancers/testing/fixtures.go b/openstack/loadbalancer/v2/loadbalancers/testing/fixtures_test.go similarity index 100% rename from openstack/loadbalancer/v2/loadbalancers/testing/fixtures.go rename to openstack/loadbalancer/v2/loadbalancers/testing/fixtures_test.go diff --git a/openstack/loadbalancer/v2/monitors/testing/fixtures.go b/openstack/loadbalancer/v2/monitors/testing/fixtures_test.go similarity index 100% rename from openstack/loadbalancer/v2/monitors/testing/fixtures.go rename to openstack/loadbalancer/v2/monitors/testing/fixtures_test.go diff --git a/openstack/loadbalancer/v2/pools/testing/fixtures.go b/openstack/loadbalancer/v2/pools/testing/fixtures_test.go similarity index 100% rename from openstack/loadbalancer/v2/pools/testing/fixtures.go rename to openstack/loadbalancer/v2/pools/testing/fixtures_test.go diff --git a/openstack/loadbalancer/v2/providers/testing/fixtures.go b/openstack/loadbalancer/v2/providers/testing/fixtures_test.go similarity index 100% rename from openstack/loadbalancer/v2/providers/testing/fixtures.go rename to openstack/loadbalancer/v2/providers/testing/fixtures_test.go diff --git a/openstack/loadbalancer/v2/quotas/testing/fixtures.go b/openstack/loadbalancer/v2/quotas/testing/fixtures_test.go similarity index 100% rename from openstack/loadbalancer/v2/quotas/testing/fixtures.go rename to openstack/loadbalancer/v2/quotas/testing/fixtures_test.go diff --git a/openstack/messaging/v2/claims/testing/fixtures.go b/openstack/messaging/v2/claims/testing/fixtures_test.go similarity index 100% rename from openstack/messaging/v2/claims/testing/fixtures.go rename to openstack/messaging/v2/claims/testing/fixtures_test.go diff --git a/openstack/messaging/v2/messages/testing/fixtures.go b/openstack/messaging/v2/messages/testing/fixtures_test.go similarity index 100% rename from openstack/messaging/v2/messages/testing/fixtures.go rename to openstack/messaging/v2/messages/testing/fixtures_test.go diff --git a/openstack/messaging/v2/queues/testing/fixtures.go b/openstack/messaging/v2/queues/testing/fixtures_test.go similarity index 100% rename from openstack/messaging/v2/queues/testing/fixtures.go rename to openstack/messaging/v2/queues/testing/fixtures_test.go diff --git a/openstack/networking/v2/extensions/agents/testing/fixtures.go b/openstack/networking/v2/extensions/agents/testing/fixtures_test.go similarity index 100% rename from openstack/networking/v2/extensions/agents/testing/fixtures.go rename to openstack/networking/v2/extensions/agents/testing/fixtures_test.go diff --git a/openstack/networking/v2/extensions/attributestags/testing/fixtures.go b/openstack/networking/v2/extensions/attributestags/testing/fixtures_test.go similarity index 100% rename from openstack/networking/v2/extensions/attributestags/testing/fixtures.go rename to openstack/networking/v2/extensions/attributestags/testing/fixtures_test.go diff --git a/openstack/networking/v2/extensions/dns/testing/fixtures.go b/openstack/networking/v2/extensions/dns/testing/fixtures_test.go similarity index 100% rename from openstack/networking/v2/extensions/dns/testing/fixtures.go rename to openstack/networking/v2/extensions/dns/testing/fixtures_test.go diff --git a/openstack/networking/v2/extensions/external/testing/fixtures.go b/openstack/networking/v2/extensions/external/testing/fixtures_test.go similarity index 100% rename from openstack/networking/v2/extensions/external/testing/fixtures.go rename to openstack/networking/v2/extensions/external/testing/fixtures_test.go diff --git a/openstack/networking/v2/extensions/layer3/addressscopes/testing/fixtures.go b/openstack/networking/v2/extensions/layer3/addressscopes/testing/fixtures_test.go similarity index 100% rename from openstack/networking/v2/extensions/layer3/addressscopes/testing/fixtures.go rename to openstack/networking/v2/extensions/layer3/addressscopes/testing/fixtures_test.go diff --git a/openstack/networking/v2/extensions/layer3/portforwarding/testing/fixtures.go b/openstack/networking/v2/extensions/layer3/portforwarding/testing/fixtures_test.go similarity index 100% rename from openstack/networking/v2/extensions/layer3/portforwarding/testing/fixtures.go rename to openstack/networking/v2/extensions/layer3/portforwarding/testing/fixtures_test.go diff --git a/openstack/networking/v2/extensions/lbaas_v2/l7policies/testing/fixtures.go b/openstack/networking/v2/extensions/lbaas_v2/l7policies/testing/fixtures_test.go similarity index 100% rename from openstack/networking/v2/extensions/lbaas_v2/l7policies/testing/fixtures.go rename to openstack/networking/v2/extensions/lbaas_v2/l7policies/testing/fixtures_test.go diff --git a/openstack/networking/v2/extensions/lbaas_v2/listeners/testing/fixtures.go b/openstack/networking/v2/extensions/lbaas_v2/listeners/testing/fixtures_test.go similarity index 100% rename from openstack/networking/v2/extensions/lbaas_v2/listeners/testing/fixtures.go rename to openstack/networking/v2/extensions/lbaas_v2/listeners/testing/fixtures_test.go diff --git a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/testing/fixtures.go b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/testing/fixtures_test.go similarity index 100% rename from openstack/networking/v2/extensions/lbaas_v2/loadbalancers/testing/fixtures.go rename to openstack/networking/v2/extensions/lbaas_v2/loadbalancers/testing/fixtures_test.go diff --git a/openstack/networking/v2/extensions/lbaas_v2/monitors/testing/fixtures.go b/openstack/networking/v2/extensions/lbaas_v2/monitors/testing/fixtures_test.go similarity index 100% rename from openstack/networking/v2/extensions/lbaas_v2/monitors/testing/fixtures.go rename to openstack/networking/v2/extensions/lbaas_v2/monitors/testing/fixtures_test.go diff --git a/openstack/networking/v2/extensions/lbaas_v2/pools/testing/fixtures.go b/openstack/networking/v2/extensions/lbaas_v2/pools/testing/fixtures_test.go similarity index 100% rename from openstack/networking/v2/extensions/lbaas_v2/pools/testing/fixtures.go rename to openstack/networking/v2/extensions/lbaas_v2/pools/testing/fixtures_test.go diff --git a/openstack/networking/v2/extensions/mtu/testing/fixtures.go b/openstack/networking/v2/extensions/mtu/testing/fixtures_test.go similarity index 100% rename from openstack/networking/v2/extensions/mtu/testing/fixtures.go rename to openstack/networking/v2/extensions/mtu/testing/fixtures_test.go diff --git a/openstack/networking/v2/extensions/networkipavailabilities/testing/fixtures.go b/openstack/networking/v2/extensions/networkipavailabilities/testing/fixtures_test.go similarity index 100% rename from openstack/networking/v2/extensions/networkipavailabilities/testing/fixtures.go rename to openstack/networking/v2/extensions/networkipavailabilities/testing/fixtures_test.go diff --git a/openstack/networking/v2/extensions/portsbinding/testing/fixtures.go b/openstack/networking/v2/extensions/portsbinding/testing/fixtures_test.go similarity index 100% rename from openstack/networking/v2/extensions/portsbinding/testing/fixtures.go rename to openstack/networking/v2/extensions/portsbinding/testing/fixtures_test.go diff --git a/openstack/networking/v2/extensions/qos/policies/testing/fixtures.go b/openstack/networking/v2/extensions/qos/policies/testing/fixtures_test.go similarity index 100% rename from openstack/networking/v2/extensions/qos/policies/testing/fixtures.go rename to openstack/networking/v2/extensions/qos/policies/testing/fixtures_test.go diff --git a/openstack/networking/v2/extensions/qos/rules/testing/fixtures.go b/openstack/networking/v2/extensions/qos/rules/testing/fixtures_test.go similarity index 100% rename from openstack/networking/v2/extensions/qos/rules/testing/fixtures.go rename to openstack/networking/v2/extensions/qos/rules/testing/fixtures_test.go diff --git a/openstack/networking/v2/extensions/qos/ruletypes/testing/fixtures.go b/openstack/networking/v2/extensions/qos/ruletypes/testing/fixtures_test.go similarity index 100% rename from openstack/networking/v2/extensions/qos/ruletypes/testing/fixtures.go rename to openstack/networking/v2/extensions/qos/ruletypes/testing/fixtures_test.go diff --git a/openstack/networking/v2/extensions/quotas/testing/fixtures.go b/openstack/networking/v2/extensions/quotas/testing/fixtures_test.go similarity index 100% rename from openstack/networking/v2/extensions/quotas/testing/fixtures.go rename to openstack/networking/v2/extensions/quotas/testing/fixtures_test.go diff --git a/openstack/networking/v2/extensions/rbacpolicies/testing/fixtures.go b/openstack/networking/v2/extensions/rbacpolicies/testing/fixtures_test.go similarity index 100% rename from openstack/networking/v2/extensions/rbacpolicies/testing/fixtures.go rename to openstack/networking/v2/extensions/rbacpolicies/testing/fixtures_test.go diff --git a/openstack/networking/v2/extensions/security/groups/testing/fixtures.go b/openstack/networking/v2/extensions/security/groups/testing/fixtures_test.go similarity index 100% rename from openstack/networking/v2/extensions/security/groups/testing/fixtures.go rename to openstack/networking/v2/extensions/security/groups/testing/fixtures_test.go diff --git a/openstack/networking/v2/extensions/subnetpools/testing/fixtures.go b/openstack/networking/v2/extensions/subnetpools/testing/fixtures_test.go similarity index 100% rename from openstack/networking/v2/extensions/subnetpools/testing/fixtures.go rename to openstack/networking/v2/extensions/subnetpools/testing/fixtures_test.go diff --git a/openstack/networking/v2/extensions/trunk_details/testing/fixtures.go b/openstack/networking/v2/extensions/trunk_details/testing/fixtures_test.go similarity index 100% rename from openstack/networking/v2/extensions/trunk_details/testing/fixtures.go rename to openstack/networking/v2/extensions/trunk_details/testing/fixtures_test.go diff --git a/openstack/networking/v2/extensions/trunks/testing/fixtures.go b/openstack/networking/v2/extensions/trunks/testing/fixtures_test.go similarity index 100% rename from openstack/networking/v2/extensions/trunks/testing/fixtures.go rename to openstack/networking/v2/extensions/trunks/testing/fixtures_test.go diff --git a/openstack/networking/v2/extensions/vlantransparent/testing/fixtures.go b/openstack/networking/v2/extensions/vlantransparent/testing/fixtures_test.go similarity index 100% rename from openstack/networking/v2/extensions/vlantransparent/testing/fixtures.go rename to openstack/networking/v2/extensions/vlantransparent/testing/fixtures_test.go diff --git a/openstack/networking/v2/subnets/testing/fixtures.go b/openstack/networking/v2/subnets/testing/fixtures_test.go similarity index 100% rename from openstack/networking/v2/subnets/testing/fixtures.go rename to openstack/networking/v2/subnets/testing/fixtures_test.go diff --git a/openstack/objectstorage/v1/objects/testing/fixtures.go b/openstack/objectstorage/v1/objects/testing/fixtures_test.go similarity index 100% rename from openstack/objectstorage/v1/objects/testing/fixtures.go rename to openstack/objectstorage/v1/objects/testing/fixtures_test.go diff --git a/openstack/objectstorage/v1/swauth/testing/fixtures.go b/openstack/objectstorage/v1/swauth/testing/fixtures_test.go similarity index 100% rename from openstack/objectstorage/v1/swauth/testing/fixtures.go rename to openstack/objectstorage/v1/swauth/testing/fixtures_test.go diff --git a/openstack/orchestration/v1/buildinfo/testing/fixtures.go b/openstack/orchestration/v1/buildinfo/testing/fixtures_test.go similarity index 100% rename from openstack/orchestration/v1/buildinfo/testing/fixtures.go rename to openstack/orchestration/v1/buildinfo/testing/fixtures_test.go diff --git a/openstack/orchestration/v1/resourcetypes/testing/fixtures.go b/openstack/orchestration/v1/resourcetypes/testing/fixtures_test.go similarity index 100% rename from openstack/orchestration/v1/resourcetypes/testing/fixtures.go rename to openstack/orchestration/v1/resourcetypes/testing/fixtures_test.go diff --git a/openstack/orchestration/v1/stackevents/testing/fixtures.go b/openstack/orchestration/v1/stackevents/testing/fixtures_test.go similarity index 100% rename from openstack/orchestration/v1/stackevents/testing/fixtures.go rename to openstack/orchestration/v1/stackevents/testing/fixtures_test.go diff --git a/openstack/orchestration/v1/stackresources/testing/fixtures.go b/openstack/orchestration/v1/stackresources/testing/fixtures_test.go similarity index 100% rename from openstack/orchestration/v1/stackresources/testing/fixtures.go rename to openstack/orchestration/v1/stackresources/testing/fixtures_test.go diff --git a/openstack/orchestration/v1/stacks/testing/fixtures.go b/openstack/orchestration/v1/stacks/testing/fixtures_test.go similarity index 100% rename from openstack/orchestration/v1/stacks/testing/fixtures.go rename to openstack/orchestration/v1/stacks/testing/fixtures_test.go diff --git a/openstack/orchestration/v1/stacktemplates/testing/fixtures.go b/openstack/orchestration/v1/stacktemplates/testing/fixtures_test.go similarity index 100% rename from openstack/orchestration/v1/stacktemplates/testing/fixtures.go rename to openstack/orchestration/v1/stacktemplates/testing/fixtures_test.go diff --git a/openstack/placement/v1/resourceproviders/testing/fixtures.go b/openstack/placement/v1/resourceproviders/testing/fixtures_test.go similarity index 100% rename from openstack/placement/v1/resourceproviders/testing/fixtures.go rename to openstack/placement/v1/resourceproviders/testing/fixtures_test.go diff --git a/openstack/sharedfilesystems/apiversions/testing/fixtures.go b/openstack/sharedfilesystems/apiversions/testing/fixtures_test.go similarity index 100% rename from openstack/sharedfilesystems/apiversions/testing/fixtures.go rename to openstack/sharedfilesystems/apiversions/testing/fixtures_test.go diff --git a/openstack/sharedfilesystems/v2/availabilityzones/testing/fixtures.go b/openstack/sharedfilesystems/v2/availabilityzones/testing/fixtures_test.go similarity index 100% rename from openstack/sharedfilesystems/v2/availabilityzones/testing/fixtures.go rename to openstack/sharedfilesystems/v2/availabilityzones/testing/fixtures_test.go diff --git a/openstack/sharedfilesystems/v2/errors/testing/fixtures.go b/openstack/sharedfilesystems/v2/errors/testing/fixtures_test.go similarity index 100% rename from openstack/sharedfilesystems/v2/errors/testing/fixtures.go rename to openstack/sharedfilesystems/v2/errors/testing/fixtures_test.go diff --git a/openstack/sharedfilesystems/v2/messages/testing/fixtures.go b/openstack/sharedfilesystems/v2/messages/testing/fixtures_test.go similarity index 100% rename from openstack/sharedfilesystems/v2/messages/testing/fixtures.go rename to openstack/sharedfilesystems/v2/messages/testing/fixtures_test.go diff --git a/openstack/sharedfilesystems/v2/replicas/testing/fixtures.go b/openstack/sharedfilesystems/v2/replicas/testing/fixtures_test.go similarity index 100% rename from openstack/sharedfilesystems/v2/replicas/testing/fixtures.go rename to openstack/sharedfilesystems/v2/replicas/testing/fixtures_test.go diff --git a/openstack/sharedfilesystems/v2/schedulerstats/testing/fixtures.go b/openstack/sharedfilesystems/v2/schedulerstats/testing/fixtures_test.go similarity index 100% rename from openstack/sharedfilesystems/v2/schedulerstats/testing/fixtures.go rename to openstack/sharedfilesystems/v2/schedulerstats/testing/fixtures_test.go diff --git a/openstack/sharedfilesystems/v2/securityservices/testing/fixtures.go b/openstack/sharedfilesystems/v2/securityservices/testing/fixtures_test.go similarity index 100% rename from openstack/sharedfilesystems/v2/securityservices/testing/fixtures.go rename to openstack/sharedfilesystems/v2/securityservices/testing/fixtures_test.go diff --git a/openstack/sharedfilesystems/v2/services/testing/fixtures.go b/openstack/sharedfilesystems/v2/services/testing/fixtures_test.go similarity index 100% rename from openstack/sharedfilesystems/v2/services/testing/fixtures.go rename to openstack/sharedfilesystems/v2/services/testing/fixtures_test.go diff --git a/openstack/sharedfilesystems/v2/shareaccessrules/testing/fixtures.go b/openstack/sharedfilesystems/v2/shareaccessrules/testing/fixtures_test.go similarity index 100% rename from openstack/sharedfilesystems/v2/shareaccessrules/testing/fixtures.go rename to openstack/sharedfilesystems/v2/shareaccessrules/testing/fixtures_test.go diff --git a/openstack/sharedfilesystems/v2/sharenetworks/testing/fixtures.go b/openstack/sharedfilesystems/v2/sharenetworks/testing/fixtures_test.go similarity index 100% rename from openstack/sharedfilesystems/v2/sharenetworks/testing/fixtures.go rename to openstack/sharedfilesystems/v2/sharenetworks/testing/fixtures_test.go diff --git a/openstack/sharedfilesystems/v2/shares/testing/fixtures.go b/openstack/sharedfilesystems/v2/shares/testing/fixtures_test.go similarity index 100% rename from openstack/sharedfilesystems/v2/shares/testing/fixtures.go rename to openstack/sharedfilesystems/v2/shares/testing/fixtures_test.go diff --git a/openstack/sharedfilesystems/v2/sharetransfers/testing/fixtures.go b/openstack/sharedfilesystems/v2/sharetransfers/testing/fixtures_test.go similarity index 100% rename from openstack/sharedfilesystems/v2/sharetransfers/testing/fixtures.go rename to openstack/sharedfilesystems/v2/sharetransfers/testing/fixtures_test.go diff --git a/openstack/sharedfilesystems/v2/sharetypes/testing/fixtures.go b/openstack/sharedfilesystems/v2/sharetypes/testing/fixtures_test.go similarity index 100% rename from openstack/sharedfilesystems/v2/sharetypes/testing/fixtures.go rename to openstack/sharedfilesystems/v2/sharetypes/testing/fixtures_test.go diff --git a/openstack/sharedfilesystems/v2/snapshots/testing/fixtures.go b/openstack/sharedfilesystems/v2/snapshots/testing/fixtures_test.go similarity index 100% rename from openstack/sharedfilesystems/v2/snapshots/testing/fixtures.go rename to openstack/sharedfilesystems/v2/snapshots/testing/fixtures_test.go From fc46027be8dd1e15fab6782d863017c74e8a9bde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= <m.andre@redhat.com> Date: Fri, 20 Oct 2023 14:27:29 +0200 Subject: [PATCH 315/360] Test files alongside code Prior to this patch, all test files sitting alongside the code, outside of a `testing` directory were ignored by the CI scripts. --- script/coverage | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/script/coverage b/script/coverage index 3efa81ba5a..6b76a1be7b 100755 --- a/script/coverage +++ b/script/coverage @@ -5,8 +5,17 @@ set -e n=1 for testpkg in $(go list ./testing ./.../testing); do covpkg="${testpkg/"/testing"/}" - go test -covermode count -coverprofile "testing_"$n.coverprofile -coverpkg $covpkg $testpkg 2>/dev/null + go test -covermode count -coverprofile "testing_"$n.coverprofile -coverpkg "$covpkg" "$testpkg" 2>/dev/null n=$((n+1)) done + +base_pkg=$(go list) +# Look for additional test files +for path in $(find . -path '*/testing' -prune -o -path '*/internal' -prune -o -name '*_test.go' -exec dirname {} \; | uniq); do + pkg="${base_pkg}${path:1}" + go test -covermode count -coverprofile "testing_"$n.coverprofile -coverpkg "$pkg" "$pkg" 2>/dev/null + n=$((n+1)) +done + gocovmerge `ls *.coverprofile` > cover.out -rm *.coverprofile +rm ./*.coverprofile From 04f05f1f666d972082c077a0bb0772a8463b78a0 Mon Sep 17 00:00:00 2001 From: Emilien Macchi <emacchi@redhat.com> Date: Tue, 24 Oct 2023 11:31:43 -0400 Subject: [PATCH 316/360] Add more godoc to GuestFormat I recently found out that when not providing the GuestFormat to a block device mapping, the device will be formated to `vfat` anyway. https://opendev.org/openstack/nova/src/commit/d0b459423dd81644e8d9382b6c87fabaa4f03ad4/nova/privsep/fs.py#L257 I think it's valuable that we document that so the users know what to expect. --- openstack/compute/v2/extensions/bootfromvolume/requests.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/openstack/compute/v2/extensions/bootfromvolume/requests.go b/openstack/compute/v2/extensions/bootfromvolume/requests.go index 17f11b3849..05f45aeceb 100644 --- a/openstack/compute/v2/extensions/bootfromvolume/requests.go +++ b/openstack/compute/v2/extensions/bootfromvolume/requests.go @@ -62,6 +62,9 @@ type BlockDevice struct { 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 From 3df6ee567b4c5c1e645883afd042503d9ae424b1 Mon Sep 17 00:00:00 2001 From: Pierre Prinetti <pierreprinetti@redhat.com> Date: Thu, 26 Oct 2023 00:57:59 +0200 Subject: [PATCH 317/360] Allow objects.CreateTempURL with names containing '/v1/' Before this patch, if an object's name or container name contained the string `/v1/`, it was not possible to create a TempURL for it. This was due to a bug in how the URL was split between the base part, and the object-path part. With this patch, container names or object names containing `/v1/` can have a TempURL created successfully. --- .../acceptance/openstack/objectstorage/v1/objects_test.go | 4 +++- openstack/objectstorage/v1/objects/requests.go | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/internal/acceptance/openstack/objectstorage/v1/objects_test.go b/internal/acceptance/openstack/objectstorage/v1/objects_test.go index e60fba9178..86db2c6575 100644 --- a/internal/acceptance/openstack/objectstorage/v1/objects_test.go +++ b/internal/acceptance/openstack/objectstorage/v1/objects_test.go @@ -22,6 +22,7 @@ import ( var numObjects = 2 func TestObjects(t *testing.T) { + numObjects := numObjects + 1 client, err := clients.NewObjectStorageV1Client() if err != nil { t.Fatalf("Unable to create client: %v", err) @@ -29,9 +30,10 @@ func TestObjects(t *testing.T) { // Make a slice of length numObjects to hold the random object names. oNames := make([]string, numObjects) - for i := 0; i < len(oNames); i++ { + for i := 0; i < len(oNames)-1; i++ { oNames[i] = tools.RandomString("test-object-", 8) } + oNames[len(oNames)-1] = "test-object-with-/v1/-in-the-name" // Create a container to hold the test objects. cName := tools.RandomString("test-container-", 8) diff --git a/openstack/objectstorage/v1/objects/requests.go b/openstack/objectstorage/v1/objects/requests.go index c03a27fce5..ed9bd180f9 100644 --- a/openstack/objectstorage/v1/objects/requests.go +++ b/openstack/objectstorage/v1/objects/requests.go @@ -612,7 +612,7 @@ func CreateTempURL(c *gophercloud.ServiceClient, containerName, objectName strin } secretKey := []byte(tempURLKey) - splitPath := strings.Split(url, opts.Split) + splitPath := strings.SplitN(url, opts.Split, 2) baseURL, objectPath := splitPath[0], splitPath[1] objectPath = opts.Split + objectPath body := fmt.Sprintf("%s\n%d\n%s", opts.Method, expiry, objectPath) From ccfa81e1fdd5b411b3586417f2d946a66577e53e Mon Sep 17 00:00:00 2001 From: Pierre Prinetti <pierreprinetti@redhat.com> Date: Thu, 30 Nov 2023 10:26:02 +0100 Subject: [PATCH 318/360] unit tests: Fix the installation of tools Install Go tools with new newest Go version before switching to the target version of Go to run the unit tests. This commit fixes an issue where the Go tools we use in unit tests can't be compiled with Go v1.14 any more. --- .github/workflows/unit.yml | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/.github/workflows/unit.yml b/.github/workflows/unit.yml index b438f33a4b..4a81289df4 100644 --- a/.github/workflows/unit.yml +++ b/.github/workflows/unit.yml @@ -20,22 +20,23 @@ jobs: GO111MODULE: "on" steps: - - name: Setup Go ${{ matrix.go-version }} + - uses: actions/checkout@v3 + + - name: Setup Go v1 uses: actions/setup-go@v4 with: - go-version: ${{ matrix.go-version }} - - - uses: actions/checkout@v3 + go-version: 1 - - name: Setup environment + - name: Install tools run: | - # Changing into a different directory to avoid polluting go.sum with "go get" - cd "$(mktemp -d)" - go mod init unit_tests + go install github.com/wadey/gocovmerge@master + go install golang.org/x/tools/cmd/goimports@latest - # we use "go get" for Go v1.14 - go install github.com/wadey/gocovmerge@master || go get github.com/wadey/gocovmerge - go install golang.org/x/tools/cmd/goimports@latest || go get golang.org/x/tools/cmd/goimports@v0.8.0 + - if: ${{ matrix.go-version != '1' }} + name: Setup Go ${{ matrix.go-version }} + uses: actions/setup-go@v4 + with: + go-version: ${{ matrix.go-version }} - name: Run go vet run: | @@ -43,6 +44,7 @@ jobs: - name: Run unit tests run: | + go version ./script/coverage ./script/format ./script/unittest -v From 796aba14dc71b3c5e3a4486178789c8dcca8c7b2 Mon Sep 17 00:00:00 2001 From: Pierre Prinetti <pierreprinetti@redhat.com> Date: Thu, 30 Nov 2023 10:10:11 +0100 Subject: [PATCH 319/360] Prepare the v1.8.0 release --- CHANGELOG.md | 16 ++++++++++++++++ provider_client.go | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b470df398b..e13f9e17ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,19 @@ +## 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: diff --git a/provider_client.go b/provider_client.go index e53b713cd4..ce291a3ab3 100644 --- a/provider_client.go +++ b/provider_client.go @@ -14,7 +14,7 @@ import ( // DefaultUserAgent is the default User-Agent string set in the request header. const ( - DefaultUserAgent = "gophercloud/v1.7.0" + DefaultUserAgent = "gophercloud/v1.8.0" DefaultMaxBackoffRetries = 60 ) From 95a9036231ef715fe96cf3221e5fc75a90aba9c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= <m.andre@redhat.com> Date: Mon, 1 Jan 2024 14:38:16 +0100 Subject: [PATCH 320/360] Fix devstack install on EOL magnum branches The stable victoria, wallaby, and xena branches are no longer available upstream for magnum. We need to use their respective `-eol` tag to keep using them in our CI jobs. --- .../workflows/functional-containerinfra.yaml | 27 +++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/.github/workflows/functional-containerinfra.yaml b/.github/workflows/functional-containerinfra.yaml index 2c7b4a3480..52ba96883e 100644 --- a/.github/workflows/functional-containerinfra.yaml +++ b/.github/workflows/functional-containerinfra.yaml @@ -12,27 +12,51 @@ jobs: - name: "master" openstack_version: "master" ubuntu_version: "22.04" + devstack_conf_overrides: | + enable_plugin magnum https://github.com/openstack/magnum master + MAGNUMCLIENT_BRANCH=master - name: "bobcat" openstack_version: "stable/2023.2" ubuntu_version: "22.04" + devstack_conf_overrides: | + enable_plugin magnum https://github.com/openstack/magnum stable/2023.2 + MAGNUMCLIENT_BRANCH=stable/2023.2 - name: "antelope" openstack_version: "stable/2023.1" ubuntu_version: "22.04" + devstack_conf_overrides: | + enable_plugin magnum https://github.com/openstack/magnum stable/2023.1 + MAGNUMCLIENT_BRANCH=stable/2023.1 - name: "zed" openstack_version: "stable/zed" ubuntu_version: "20.04" + devstack_conf_overrides: | + enable_plugin magnum https://github.com/openstack/magnum stable/zed + MAGNUMCLIENT_BRANCH=stable/zed - name: "yoga" openstack_version: "stable/yoga" ubuntu_version: "20.04" + devstack_conf_overrides: | + enable_plugin magnum https://github.com/openstack/magnum stable/yoga + MAGNUMCLIENT_BRANCH=stable/yoga - name: "xena" openstack_version: "stable/xena" ubuntu_version: "20.04" + devstack_conf_overrides: | + enable_plugin magnum https://github.com/openstack/magnum xena-eol + MAGNUMCLIENT_BRANCH=xena-eol - name: "wallaby" openstack_version: "stable/wallaby" ubuntu_version: "20.04" + devstack_conf_overrides: | + enable_plugin magnum https://github.com/openstack/magnum wallaby-eol + MAGNUMCLIENT_BRANCH=wallaby-eol - name: "victoria" openstack_version: "stable/victoria" ubuntu_version: "20.04" + devstack_conf_overrides: | + enable_plugin magnum https://github.com/openstack/magnum victoria-eol + MAGNUMCLIENT_BRANCH=victoria-em runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Deploy OpenStack ${{ matrix.name }} with Magnum and run containerinfra acceptance tests steps: @@ -43,13 +67,12 @@ jobs: with: branch: ${{ matrix.openstack_version }} conf_overrides: | - enable_plugin magnum https://github.com/openstack/magnum ${{ matrix.openstack_version }} enable_plugin barbican https://github.com/openstack/barbican ${{ matrix.openstack_version }} enable_plugin heat https://github.com/openstack/heat ${{ matrix.openstack_version }} GLANCE_LIMIT_IMAGE_SIZE_TOTAL=5000 SWIFT_MAX_FILE_SIZE=5368709122 KEYSTONE_ADMIN_ENDPOINT=true - MAGNUMCLIENT_BRANCH=${{ matrix.openstack_version }} + ${{ matrix.devstack_conf_overrides }} enabled_services: 'h-eng,h-api,h-api-cfn,h-api-cw' - name: Checkout go uses: actions/setup-go@v4 From 3c68a0fa27cbb5666baa99566080363f64591d1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= <m.andre@redhat.com> Date: Mon, 1 Jan 2024 14:07:01 +0100 Subject: [PATCH 321/360] Fix networking acceptance tests Since https://review.opendev.org/c/openstack/neutron/+/892815 it is no longer possible to update the vnic type of a bound port. These doesn't seem to be an API to unbind the port other than deleting the port, so instead we're dropping the `VNICType` attribute from the update request in the `TestPortsbindingCRUD` test. --- .../networking/v2/extensions/portsbinding/portsbinding_test.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/internal/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding_test.go b/internal/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding_test.go index 50f5e8c99a..56f4d6530f 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding_test.go @@ -59,7 +59,6 @@ func TestPortsbindingCRUD(t *testing.T) { finalUpdateOpts = portsbinding.UpdateOptsExt{ UpdateOptsBuilder: updateOpts, HostID: &newHostID, - VNICType: "baremetal", Profile: newProfile, } @@ -76,6 +75,6 @@ func TestPortsbindingCRUD(t *testing.T) { th.AssertEquals(t, newPort.Description, newPortName) th.AssertEquals(t, newPort.Description, newPortDescription) th.AssertEquals(t, newPort.HostID, newHostID) - th.AssertEquals(t, newPort.VNICType, "baremetal") + th.AssertEquals(t, newPort.VNICType, "normal") th.AssertDeepEquals(t, newPort.Profile, newProfile) } From e707ba8798c92552ef494cb048a4aeada94a70c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= <m.andre@redhat.com> Date: Mon, 1 Jan 2024 14:48:11 +0100 Subject: [PATCH 322/360] TestPortsbindingCRUD: Fix order of arguments in equal assertions The correct order is `expected`, `actual`. Having this right helps debugging since the values are printed in a log message. --- .../extensions/portsbinding/portsbinding_test.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/internal/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding_test.go b/internal/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding_test.go index 56f4d6530f..e4e851fe28 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding_test.go @@ -40,9 +40,9 @@ func TestPortsbindingCRUD(t *testing.T) { defer networking.DeletePort(t, client, port.ID) tools.PrintResource(t, port) - th.AssertEquals(t, port.HostID, hostID) - th.AssertEquals(t, port.VNICType, "normal") - th.AssertDeepEquals(t, port.Profile, profile) + th.AssertEquals(t, hostID, port.HostID) + th.AssertEquals(t, "normal", port.VNICType) + th.AssertDeepEquals(t, profile, port.Profile) // Update port newPortName := "" @@ -72,9 +72,9 @@ func TestPortsbindingCRUD(t *testing.T) { th.AssertNoErr(t, err) tools.PrintResource(t, newPort) - th.AssertEquals(t, newPort.Description, newPortName) - th.AssertEquals(t, newPort.Description, newPortDescription) - th.AssertEquals(t, newPort.HostID, newHostID) - th.AssertEquals(t, newPort.VNICType, "normal") - th.AssertDeepEquals(t, newPort.Profile, newProfile) + th.AssertEquals(t, newPortName, newPort.Description) + th.AssertEquals(t, newPortDescription, newPort.Description) + th.AssertEquals(t, newHostID, newPort.HostID) + th.AssertEquals(t, "normal", newPort.VNICType) + th.AssertDeepEquals(t, newProfile, newPort.Profile) } From 5e7274381f13980c6d90ddc35a2b6021cfcf7c9d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 15 Dec 2023 09:46:36 +0000 Subject: [PATCH 323/360] build(deps): bump actions/upload-artifact from 3 to 4 Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 3 to 4. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> --- .github/workflows/functional-baremetal.yaml | 2 +- .github/workflows/functional-basic.yaml | 2 +- .github/workflows/functional-blockstorage.yaml | 2 +- .github/workflows/functional-clustering.yaml | 2 +- .github/workflows/functional-compute.yaml | 2 +- .github/workflows/functional-containerinfra.yaml | 2 +- .github/workflows/functional-dns.yaml | 2 +- .github/workflows/functional-fwaas_v2.yaml | 2 +- .github/workflows/functional-identity.yaml | 2 +- .github/workflows/functional-imageservice.yaml | 2 +- .github/workflows/functional-keymanager.yaml | 2 +- .github/workflows/functional-loadbalancer.yaml | 2 +- .github/workflows/functional-messaging.yaml | 2 +- .github/workflows/functional-networking.yaml | 2 +- .github/workflows/functional-objectstorage.yaml | 2 +- .github/workflows/functional-orchestration.yaml | 2 +- .github/workflows/functional-placement.yaml | 2 +- .github/workflows/functional-sharedfilesystems.yaml | 2 +- 18 files changed, 18 insertions(+), 18 deletions(-) diff --git a/.github/workflows/functional-baremetal.yaml b/.github/workflows/functional-baremetal.yaml index 6a8eb98fcf..f66da92a26 100644 --- a/.github/workflows/functional-baremetal.yaml +++ b/.github/workflows/functional-baremetal.yaml @@ -92,7 +92,7 @@ jobs: if: failure() - name: Upload logs artifacts on failure if: failure() - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: functional-baremetal-${{ matrix.name }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-basic.yaml b/.github/workflows/functional-basic.yaml index 879bd678ad..a679a0f27e 100644 --- a/.github/workflows/functional-basic.yaml +++ b/.github/workflows/functional-basic.yaml @@ -61,7 +61,7 @@ jobs: if: failure() - name: Upload logs artifacts on failure if: failure() - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: functional-basic-${{ matrix.name }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-blockstorage.yaml b/.github/workflows/functional-blockstorage.yaml index e34f2c3de8..b0b5f759ec 100644 --- a/.github/workflows/functional-blockstorage.yaml +++ b/.github/workflows/functional-blockstorage.yaml @@ -60,7 +60,7 @@ jobs: if: failure() - name: Upload logs artifacts on failure if: failure() - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: functional-blockstorage-${{ matrix.name }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-clustering.yaml b/.github/workflows/functional-clustering.yaml index 6d86dc6880..c7d7a53c65 100644 --- a/.github/workflows/functional-clustering.yaml +++ b/.github/workflows/functional-clustering.yaml @@ -61,7 +61,7 @@ jobs: if: failure() - name: Upload logs artifacts on failure if: failure() - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: functional-clustering-${{ matrix.name }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-compute.yaml b/.github/workflows/functional-compute.yaml index cdc789793f..6cf62990ff 100644 --- a/.github/workflows/functional-compute.yaml +++ b/.github/workflows/functional-compute.yaml @@ -59,7 +59,7 @@ jobs: if: failure() - name: Upload logs artifacts on failure if: failure() - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: functional-compute-${{ matrix.name }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-containerinfra.yaml b/.github/workflows/functional-containerinfra.yaml index 52ba96883e..9ed76d8026 100644 --- a/.github/workflows/functional-containerinfra.yaml +++ b/.github/workflows/functional-containerinfra.yaml @@ -89,7 +89,7 @@ jobs: if: failure() - name: Upload logs artifacts on failure if: failure() - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: functional-containerinfra-${{ matrix.name }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-dns.yaml b/.github/workflows/functional-dns.yaml index 427832a95a..43177088c7 100644 --- a/.github/workflows/functional-dns.yaml +++ b/.github/workflows/functional-dns.yaml @@ -61,7 +61,7 @@ jobs: if: failure() - name: Upload logs artifacts on failure if: failure() - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: functional-dns-${{ matrix.name }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-fwaas_v2.yaml b/.github/workflows/functional-fwaas_v2.yaml index 17dd723f29..020163ce27 100644 --- a/.github/workflows/functional-fwaas_v2.yaml +++ b/.github/workflows/functional-fwaas_v2.yaml @@ -53,7 +53,7 @@ jobs: if: failure() - name: Upload logs artifacts on failure if: failure() - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: functional-fwaas_v2-${{ matrix.name }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-identity.yaml b/.github/workflows/functional-identity.yaml index 43e638381f..a62ddacf78 100644 --- a/.github/workflows/functional-identity.yaml +++ b/.github/workflows/functional-identity.yaml @@ -57,7 +57,7 @@ jobs: if: failure() - name: Upload logs artifacts on failure if: failure() - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: functional-identity-${{ matrix.name }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-imageservice.yaml b/.github/workflows/functional-imageservice.yaml index a85e6621e2..9dec83dd54 100644 --- a/.github/workflows/functional-imageservice.yaml +++ b/.github/workflows/functional-imageservice.yaml @@ -57,7 +57,7 @@ jobs: if: failure() - name: Upload logs artifacts on failure if: failure() - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: functional-imageservice-${{ matrix.name }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-keymanager.yaml b/.github/workflows/functional-keymanager.yaml index a1b5a4ed19..e0fb3db115 100644 --- a/.github/workflows/functional-keymanager.yaml +++ b/.github/workflows/functional-keymanager.yaml @@ -60,7 +60,7 @@ jobs: if: failure() - name: Upload logs artifacts on failure if: failure() - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: functional-keymanager-${{ matrix.name }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-loadbalancer.yaml b/.github/workflows/functional-loadbalancer.yaml index fa2a3e9493..8dc42208cf 100644 --- a/.github/workflows/functional-loadbalancer.yaml +++ b/.github/workflows/functional-loadbalancer.yaml @@ -61,7 +61,7 @@ jobs: if: failure() - name: Upload logs artifacts on failure if: failure() - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: functional-loadbalancer-${{ matrix.name }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-messaging.yaml b/.github/workflows/functional-messaging.yaml index 200ed18887..2355cfed85 100644 --- a/.github/workflows/functional-messaging.yaml +++ b/.github/workflows/functional-messaging.yaml @@ -60,7 +60,7 @@ jobs: if: failure() - name: Upload logs artifacts on failure if: failure() - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: functional-messaging-${{ matrix.name }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-networking.yaml b/.github/workflows/functional-networking.yaml index 4691455e9e..8539acc80c 100644 --- a/.github/workflows/functional-networking.yaml +++ b/.github/workflows/functional-networking.yaml @@ -62,7 +62,7 @@ jobs: if: failure() - name: Upload logs artifacts on failure if: failure() - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: functional-networking-${{ matrix.name }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-objectstorage.yaml b/.github/workflows/functional-objectstorage.yaml index c5ac5bd4a1..60cd27e9f2 100644 --- a/.github/workflows/functional-objectstorage.yaml +++ b/.github/workflows/functional-objectstorage.yaml @@ -64,7 +64,7 @@ jobs: if: failure() - name: Upload logs artifacts on failure if: failure() - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: functional-objectstorage-${{ matrix.name }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-orchestration.yaml b/.github/workflows/functional-orchestration.yaml index 0d035f6be0..73d461aac6 100644 --- a/.github/workflows/functional-orchestration.yaml +++ b/.github/workflows/functional-orchestration.yaml @@ -60,7 +60,7 @@ jobs: if: failure() - name: Upload logs artifacts on failure if: failure() - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: functional-orchestration-${{ matrix.name }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-placement.yaml b/.github/workflows/functional-placement.yaml index 66d3849a98..016157562d 100644 --- a/.github/workflows/functional-placement.yaml +++ b/.github/workflows/functional-placement.yaml @@ -57,7 +57,7 @@ jobs: if: failure() - name: Upload logs artifacts on failure if: failure() - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: functional-placement-${{ matrix.name }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-sharedfilesystems.yaml b/.github/workflows/functional-sharedfilesystems.yaml index eb42329634..6924d001fe 100644 --- a/.github/workflows/functional-sharedfilesystems.yaml +++ b/.github/workflows/functional-sharedfilesystems.yaml @@ -73,7 +73,7 @@ jobs: if: failure() - name: Upload logs artifacts on failure if: failure() - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: functional-sharedfilesystems-${{ matrix.name }} path: /tmp/devstack-logs/* From f361ee3486ab447c06e13e803ab66ff0b62a4253 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 14 Dec 2023 09:25:59 +0000 Subject: [PATCH 324/360] build(deps): bump github/codeql-action from 2 to 3 Bumps [github/codeql-action](https://github.com/github/codeql-action) from 2 to 3. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/v2...v3) --- updated-dependencies: - dependency-name: github/codeql-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> --- .github/workflows/codeql-analysis.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 38cf2bfb08..f423c1f7f4 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -21,12 +21,12 @@ jobs: uses: actions/checkout@v3 - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@v3 with: languages: ${{ matrix.language }} - name: Autobuild - uses: github/codeql-action/autobuild@v2 + uses: github/codeql-action/autobuild@v3 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@v3 From 9cfe594545858b86632eca695cd3302f24d43ca7 Mon Sep 17 00:00:00 2001 From: Eugene Zuev <zhekazuev@gmail.com> Date: Thu, 18 Jan 2024 19:24:11 +0000 Subject: [PATCH 325/360] feat: add AvailabilityZone for db/v1/instance fix: alignments fix: alignments added: AvailabilityZone for db/v1/instance --- openstack/db/v1/instances/requests.go | 6 ++++++ openstack/db/v1/instances/testing/fixtures_test.go | 1 + openstack/db/v1/instances/testing/requests_test.go | 10 ++++++---- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/openstack/db/v1/instances/requests.go b/openstack/db/v1/instances/requests.go index 73c7acc74e..26638157ca 100644 --- a/openstack/db/v1/instances/requests.go +++ b/openstack/db/v1/instances/requests.go @@ -47,6 +47,8 @@ func (opts NetworkOpts) ToMap() (map[string]interface{}, error) { // CreateOpts is the struct responsible for configuring a new database instance. type CreateOpts struct { + // The availability zone of the instance. + AvailabilityZone string `json:"availability_zone,omitempty"` // Either the integer UUID (in string form) of the flavor, or its URI // reference as specified in the response from the List() call. Required. FlavorRef string @@ -87,6 +89,10 @@ func (opts CreateOpts) ToInstanceCreateMap() (map[string]interface{}, error) { "flavorRef": opts.FlavorRef, } + if opts.AvailabilityZone != "" { + instance["availability_zone"] = opts.AvailabilityZone + } + if opts.Name != "" { instance["name"] = opts.Name } diff --git a/openstack/db/v1/instances/testing/fixtures_test.go b/openstack/db/v1/instances/testing/fixtures_test.go index 782c048dc3..7abd42e3e7 100644 --- a/openstack/db/v1/instances/testing/fixtures_test.go +++ b/openstack/db/v1/instances/testing/fixtures_test.go @@ -104,6 +104,7 @@ var instanceGet = ` var createReq = ` { "instance": { + "availability_zone": "us-east1", "databases": [ { "character_set": "utf8", diff --git a/openstack/db/v1/instances/testing/requests_test.go b/openstack/db/v1/instances/testing/requests_test.go index a1575a4bb1..62d615d7a9 100644 --- a/openstack/db/v1/instances/testing/requests_test.go +++ b/openstack/db/v1/instances/testing/requests_test.go @@ -17,8 +17,9 @@ func TestCreate(t *testing.T) { HandleCreate(t) opts := instances.CreateOpts{ - Name: "json_rack_instance", - FlavorRef: "1", + AvailabilityZone: "us-east1", + Name: "json_rack_instance", + FlavorRef: "1", Databases: db.BatchCreateOpts{ {CharSet: "utf8", Collate: "utf8_general_ci", Name: "sampledb"}, {Name: "nextround"}, @@ -48,8 +49,9 @@ func TestCreateWithFault(t *testing.T) { HandleCreateWithFault(t) opts := instances.CreateOpts{ - Name: "json_rack_instance", - FlavorRef: "1", + AvailabilityZone: "us-east1", + Name: "json_rack_instance", + FlavorRef: "1", Databases: db.BatchCreateOpts{ {CharSet: "utf8", Collate: "utf8_general_ci", Name: "sampledb"}, {Name: "nextround"}, From ef06ea2bef3b883355e3d109bbf4c48c4f40e8e0 Mon Sep 17 00:00:00 2001 From: Pierre Prinetti <pierreprinetti@redhat.com> Date: Wed, 31 Jan 2024 13:03:58 +0100 Subject: [PATCH 326/360] Context-aware methods to ProviderClient and ServiceClient Provide new methods to leverage per-call `context.Context`. This patch, suitable to be merged to `v1`, vendors the context package from the v1.21 standard library, to make `AfterFunc` available. --- internal/ctxt/context.go | 739 ++++++++++++++++++++++++++++++++++++ internal/ctxt/merge.go | 52 +++ internal/ctxt/merge_test.go | 133 +++++++ provider_client.go | 45 ++- service_client.go | 76 +++- 5 files changed, 1008 insertions(+), 37 deletions(-) create mode 100644 internal/ctxt/context.go create mode 100644 internal/ctxt/merge.go create mode 100644 internal/ctxt/merge_test.go diff --git a/internal/ctxt/context.go b/internal/ctxt/context.go new file mode 100644 index 0000000000..2352583714 --- /dev/null +++ b/internal/ctxt/context.go @@ -0,0 +1,739 @@ +package ctxt + +// This file is package context of the standard library that ships with Go +// v1.21.6. It has been vendored to import AfterFunc. +// +// Changes made to the original file: +// * replace "internal/reflectlite" with "reflect" in the imports +// * replace "any" with "interface{}" +// * remove the atomic.Int32 type that only exists for testing and is not +// compatible with Go v1.14. +// +// https://cs.opensource.google/go/go/+/refs/tags/go1.21.6:src/context/context.go + +import ( + "errors" + reflectlite "reflect" + "sync" + "sync/atomic" + "time" +) + +// A Context carries a deadline, a cancellation signal, and other values across +// API boundaries. +// +// Context's methods may be called by multiple goroutines simultaneously. +type Context interface { + // Deadline returns the time when work done on behalf of this context + // should be canceled. Deadline returns ok==false when no deadline is + // set. Successive calls to Deadline return the same results. + Deadline() (deadline time.Time, ok bool) + + // Done returns a channel that's closed when work done on behalf of this + // context should be canceled. Done may return nil if this context can + // never be canceled. Successive calls to Done return the same value. + // The close of the Done channel may happen asynchronously, + // after the cancel function returns. + // + // WithCancel arranges for Done to be closed when cancel is called; + // WithDeadline arranges for Done to be closed when the deadline + // expires; WithTimeout arranges for Done to be closed when the timeout + // elapses. + // + // Done is provided for use in select statements: + // + // // Stream generates values with DoSomething and sends them to out + // // until DoSomething returns an error or ctx.Done is closed. + // func Stream(ctx context.Context, out chan<- Value) error { + // for { + // v, err := DoSomething(ctx) + // if err != nil { + // return err + // } + // select { + // case <-ctx.Done(): + // return ctx.Err() + // case out <- v: + // } + // } + // } + // + // See https://blog.golang.org/pipelines for more examples of how to use + // a Done channel for cancellation. + Done() <-chan struct{} + + // If Done is not yet closed, Err returns nil. + // If Done is closed, Err returns a non-nil error explaining why: + // Canceled if the context was canceled + // or DeadlineExceeded if the context's deadline passed. + // After Err returns a non-nil error, successive calls to Err return the same error. + Err() error + + // Value returns the value associated with this context for key, or nil + // if no value is associated with key. Successive calls to Value with + // the same key returns the same result. + // + // Use context values only for request-scoped data that transits + // processes and API boundaries, not for passing optional parameters to + // functions. + // + // A key identifies a specific value in a Context. Functions that wish + // to store values in Context typically allocate a key in a global + // variable then use that key as the argument to context.WithValue and + // Context.Value. A key can be any type that supports equality; + // packages should define keys as an unexported type to avoid + // collisions. + // + // Packages that define a Context key should provide type-safe accessors + // for the values stored using that key: + // + // // Package user defines a User type that's stored in Contexts. + // package user + // + // import "context" + // + // // User is the type of value stored in the Contexts. + // type User struct {...} + // + // // key is an unexported type for keys defined in this package. + // // This prevents collisions with keys defined in other packages. + // type key int + // + // // userKey is the key for user.User values in Contexts. It is + // // unexported; clients use user.NewContext and user.FromContext + // // instead of using this key directly. + // var userKey key + // + // // NewContext returns a new Context that carries value u. + // func NewContext(ctx context.Context, u *User) context.Context { + // return context.WithValue(ctx, userKey, u) + // } + // + // // FromContext returns the User value stored in ctx, if any. + // func FromContext(ctx context.Context) (*User, bool) { + // u, ok := ctx.Value(userKey).(*User) + // return u, ok + // } + Value(key interface{}) interface{} +} + +// Canceled is the error returned by [Context.Err] when the context is canceled. +var Canceled = errors.New("context canceled") + +// DeadlineExceeded is the error returned by [Context.Err] when the context's +// deadline passes. +var DeadlineExceeded error = deadlineExceededError{} + +type deadlineExceededError struct{} + +func (deadlineExceededError) Error() string { return "context deadline exceeded" } +func (deadlineExceededError) Timeout() bool { return true } +func (deadlineExceededError) Temporary() bool { return true } + +// An emptyCtx is never canceled, has no values, and has no deadline. +// It is the common base of backgroundCtx and todoCtx. +type emptyCtx struct{} + +func (emptyCtx) Deadline() (deadline time.Time, ok bool) { + return +} + +func (emptyCtx) Done() <-chan struct{} { + return nil +} + +func (emptyCtx) Err() error { + return nil +} + +func (emptyCtx) Value(key interface{}) interface{} { + return nil +} + +type backgroundCtx struct{ emptyCtx } + +func (backgroundCtx) String() string { + return "context.Background" +} + +type todoCtx struct{ emptyCtx } + +func (todoCtx) String() string { + return "context.TODO" +} + +// Background returns a non-nil, empty [Context]. It is never canceled, has no +// values, and has no deadline. It is typically used by the main function, +// initialization, and tests, and as the top-level Context for incoming +// requests. +func Background() Context { + return backgroundCtx{} +} + +// TODO returns a non-nil, empty [Context]. Code should use context.TODO when +// it's unclear which Context to use or it is not yet available (because the +// surrounding function has not yet been extended to accept a Context +// parameter). +func TODO() Context { + return todoCtx{} +} + +// A CancelFunc tells an operation to abandon its work. +// A CancelFunc does not wait for the work to stop. +// A CancelFunc may be called by multiple goroutines simultaneously. +// After the first call, subsequent calls to a CancelFunc do nothing. +type CancelFunc func() + +// WithCancel returns a copy of parent with a new Done channel. The returned +// context's Done channel is closed when the returned cancel function is called +// or when the parent context's Done channel is closed, whichever happens first. +// +// Canceling this context releases resources associated with it, so code should +// call cancel as soon as the operations running in this Context complete. +func WithCancel(parent Context) (ctx Context, cancel CancelFunc) { + c := withCancel(parent) + return c, func() { c.cancel(true, Canceled, nil) } +} + +// A CancelCauseFunc behaves like a [CancelFunc] but additionally sets the cancellation cause. +// This cause can be retrieved by calling [Cause] on the canceled Context or on +// any of its derived Contexts. +// +// If the context has already been canceled, CancelCauseFunc does not set the cause. +// For example, if childContext is derived from parentContext: +// - if parentContext is canceled with cause1 before childContext is canceled with cause2, +// then Cause(parentContext) == Cause(childContext) == cause1 +// - if childContext is canceled with cause2 before parentContext is canceled with cause1, +// then Cause(parentContext) == cause1 and Cause(childContext) == cause2 +type CancelCauseFunc func(cause error) + +// WithCancelCause behaves like [WithCancel] but returns a [CancelCauseFunc] instead of a [CancelFunc]. +// Calling cancel with a non-nil error (the "cause") records that error in ctx; +// it can then be retrieved using Cause(ctx). +// Calling cancel with nil sets the cause to Canceled. +// +// Example use: +// +// ctx, cancel := context.WithCancelCause(parent) +// cancel(myError) +// ctx.Err() // returns context.Canceled +// context.Cause(ctx) // returns myError +func WithCancelCause(parent Context) (ctx Context, cancel CancelCauseFunc) { + c := withCancel(parent) + return c, func(cause error) { c.cancel(true, Canceled, cause) } +} + +func withCancel(parent Context) *cancelCtx { + if parent == nil { + panic("cannot create context from nil parent") + } + c := &cancelCtx{} + c.propagateCancel(parent, c) + return c +} + +// Cause returns a non-nil error explaining why c was canceled. +// The first cancellation of c or one of its parents sets the cause. +// If that cancellation happened via a call to CancelCauseFunc(err), +// then [Cause] returns err. +// Otherwise Cause(c) returns the same value as c.Err(). +// Cause returns nil if c has not been canceled yet. +func Cause(c Context) error { + if cc, ok := c.Value(&cancelCtxKey).(*cancelCtx); ok { + cc.mu.Lock() + defer cc.mu.Unlock() + return cc.cause + } + return nil +} + +// AfterFunc arranges to call f in its own goroutine after ctx is done +// (cancelled or timed out). +// If ctx is already done, AfterFunc calls f immediately in its own goroutine. +// +// Multiple calls to AfterFunc on a context operate independently; +// one does not replace another. +// +// Calling the returned stop function stops the association of ctx with f. +// It returns true if the call stopped f from being run. +// If stop returns false, +// either the context is done and f has been started in its own goroutine; +// or f was already stopped. +// The stop function does not wait for f to complete before returning. +// If the caller needs to know whether f is completed, +// it must coordinate with f explicitly. +// +// If ctx has a "AfterFunc(func()) func() bool" method, +// AfterFunc will use it to schedule the call. +func AfterFunc(ctx Context, f func()) (stop func() bool) { + a := &afterFuncCtx{ + f: f, + } + a.cancelCtx.propagateCancel(ctx, a) + return func() bool { + stopped := false + a.once.Do(func() { + stopped = true + }) + if stopped { + a.cancel(true, Canceled, nil) + } + return stopped + } +} + +type afterFuncer interface { + AfterFunc(func()) func() bool +} + +type afterFuncCtx struct { + cancelCtx + once sync.Once // either starts running f or stops f from running + f func() +} + +func (a *afterFuncCtx) cancel(removeFromParent bool, err, cause error) { + a.cancelCtx.cancel(false, err, cause) + if removeFromParent { + removeChild(a.Context, a) + } + a.once.Do(func() { + go a.f() + }) +} + +// A stopCtx is used as the parent context of a cancelCtx when +// an AfterFunc has been registered with the parent. +// It holds the stop function used to unregister the AfterFunc. +type stopCtx struct { + Context + stop func() bool +} + +// &cancelCtxKey is the key that a cancelCtx returns itself for. +var cancelCtxKey int + +// parentCancelCtx returns the underlying *cancelCtx for parent. +// It does this by looking up parent.Value(&cancelCtxKey) to find +// the innermost enclosing *cancelCtx and then checking whether +// parent.Done() matches that *cancelCtx. (If not, the *cancelCtx +// has been wrapped in a custom implementation providing a +// different done channel, in which case we should not bypass it.) +func parentCancelCtx(parent Context) (*cancelCtx, bool) { + done := parent.Done() + if done == closedchan || done == nil { + return nil, false + } + p, ok := parent.Value(&cancelCtxKey).(*cancelCtx) + if !ok { + return nil, false + } + pdone, _ := p.done.Load().(chan struct{}) + if pdone != done { + return nil, false + } + return p, true +} + +// removeChild removes a context from its parent. +func removeChild(parent Context, child canceler) { + if s, ok := parent.(stopCtx); ok { + s.stop() + return + } + p, ok := parentCancelCtx(parent) + if !ok { + return + } + p.mu.Lock() + if p.children != nil { + delete(p.children, child) + } + p.mu.Unlock() +} + +// A canceler is a context type that can be canceled directly. The +// implementations are *cancelCtx and *timerCtx. +type canceler interface { + cancel(removeFromParent bool, err, cause error) + Done() <-chan struct{} +} + +// closedchan is a reusable closed channel. +var closedchan = make(chan struct{}) + +func init() { + close(closedchan) +} + +// A cancelCtx can be canceled. When canceled, it also cancels any children +// that implement canceler. +type cancelCtx struct { + Context + + mu sync.Mutex // protects following fields + done atomic.Value // of chan struct{}, created lazily, closed by first cancel call + children map[canceler]struct{} // set to nil by the first cancel call + err error // set to non-nil by the first cancel call + cause error // set to non-nil by the first cancel call +} + +func (c *cancelCtx) Value(key interface{}) interface{} { + if key == &cancelCtxKey { + return c + } + return value(c.Context, key) +} + +func (c *cancelCtx) Done() <-chan struct{} { + d := c.done.Load() + if d != nil { + return d.(chan struct{}) + } + c.mu.Lock() + defer c.mu.Unlock() + d = c.done.Load() + if d == nil { + d = make(chan struct{}) + c.done.Store(d) + } + return d.(chan struct{}) +} + +func (c *cancelCtx) Err() error { + c.mu.Lock() + err := c.err + c.mu.Unlock() + return err +} + +// propagateCancel arranges for child to be canceled when parent is. +// It sets the parent context of cancelCtx. +func (c *cancelCtx) propagateCancel(parent Context, child canceler) { + c.Context = parent + + done := parent.Done() + if done == nil { + return // parent is never canceled + } + + select { + case <-done: + // parent is already canceled + child.cancel(false, parent.Err(), Cause(parent)) + return + default: + } + + if p, ok := parentCancelCtx(parent); ok { + // parent is a *cancelCtx, or derives from one. + p.mu.Lock() + if p.err != nil { + // parent has already been canceled + child.cancel(false, p.err, p.cause) + } else { + if p.children == nil { + p.children = make(map[canceler]struct{}) + } + p.children[child] = struct{}{} + } + p.mu.Unlock() + return + } + + if a, ok := parent.(afterFuncer); ok { + // parent implements an AfterFunc method. + c.mu.Lock() + stop := a.AfterFunc(func() { + child.cancel(false, parent.Err(), Cause(parent)) + }) + c.Context = stopCtx{ + Context: parent, + stop: stop, + } + c.mu.Unlock() + return + } + + go func() { + select { + case <-parent.Done(): + child.cancel(false, parent.Err(), Cause(parent)) + case <-child.Done(): + } + }() +} + +type stringer interface { + String() string +} + +func contextName(c Context) string { + if s, ok := c.(stringer); ok { + return s.String() + } + return reflectlite.TypeOf(c).String() +} + +func (c *cancelCtx) String() string { + return contextName(c.Context) + ".WithCancel" +} + +// cancel closes c.done, cancels each of c's children, and, if +// removeFromParent is true, removes c from its parent's children. +// cancel sets c.cause to cause if this is the first time c is canceled. +func (c *cancelCtx) cancel(removeFromParent bool, err, cause error) { + if err == nil { + panic("context: internal error: missing cancel error") + } + if cause == nil { + cause = err + } + c.mu.Lock() + if c.err != nil { + c.mu.Unlock() + return // already canceled + } + c.err = err + c.cause = cause + d, _ := c.done.Load().(chan struct{}) + if d == nil { + c.done.Store(closedchan) + } else { + close(d) + } + for child := range c.children { + // NOTE: acquiring the child's lock while holding parent's lock. + child.cancel(false, err, cause) + } + c.children = nil + c.mu.Unlock() + + if removeFromParent { + removeChild(c.Context, c) + } +} + +// WithoutCancel returns a copy of parent that is not canceled when parent is canceled. +// The returned context returns no Deadline or Err, and its Done channel is nil. +// Calling [Cause] on the returned context returns nil. +func WithoutCancel(parent Context) Context { + if parent == nil { + panic("cannot create context from nil parent") + } + return withoutCancelCtx{parent} +} + +type withoutCancelCtx struct { + c Context +} + +func (withoutCancelCtx) Deadline() (deadline time.Time, ok bool) { + return +} + +func (withoutCancelCtx) Done() <-chan struct{} { + return nil +} + +func (withoutCancelCtx) Err() error { + return nil +} + +func (c withoutCancelCtx) Value(key interface{}) interface{} { + return value(c, key) +} + +func (c withoutCancelCtx) String() string { + return contextName(c.c) + ".WithoutCancel" +} + +// WithDeadline returns a copy of the parent context with the deadline adjusted +// to be no later than d. If the parent's deadline is already earlier than d, +// WithDeadline(parent, d) is semantically equivalent to parent. The returned +// [Context.Done] channel is closed when the deadline expires, when the returned +// cancel function is called, or when the parent context's Done channel is +// closed, whichever happens first. +// +// Canceling this context releases resources associated with it, so code should +// call cancel as soon as the operations running in this [Context] complete. +func WithDeadline(parent Context, d time.Time) (Context, CancelFunc) { + return WithDeadlineCause(parent, d, nil) +} + +// WithDeadlineCause behaves like [WithDeadline] but also sets the cause of the +// returned Context when the deadline is exceeded. The returned [CancelFunc] does +// not set the cause. +func WithDeadlineCause(parent Context, d time.Time, cause error) (Context, CancelFunc) { + if parent == nil { + panic("cannot create context from nil parent") + } + if cur, ok := parent.Deadline(); ok && cur.Before(d) { + // The current deadline is already sooner than the new one. + return WithCancel(parent) + } + c := &timerCtx{ + deadline: d, + } + c.cancelCtx.propagateCancel(parent, c) + dur := time.Until(d) + if dur <= 0 { + c.cancel(true, DeadlineExceeded, cause) // deadline has already passed + return c, func() { c.cancel(false, Canceled, nil) } + } + c.mu.Lock() + defer c.mu.Unlock() + if c.err == nil { + c.timer = time.AfterFunc(dur, func() { + c.cancel(true, DeadlineExceeded, cause) + }) + } + return c, func() { c.cancel(true, Canceled, nil) } +} + +// A timerCtx carries a timer and a deadline. It embeds a cancelCtx to +// implement Done and Err. It implements cancel by stopping its timer then +// delegating to cancelCtx.cancel. +type timerCtx struct { + cancelCtx + timer *time.Timer // Under cancelCtx.mu. + + deadline time.Time +} + +func (c *timerCtx) Deadline() (deadline time.Time, ok bool) { + return c.deadline, true +} + +func (c *timerCtx) String() string { + return contextName(c.cancelCtx.Context) + ".WithDeadline(" + + c.deadline.String() + " [" + + time.Until(c.deadline).String() + "])" +} + +func (c *timerCtx) cancel(removeFromParent bool, err, cause error) { + c.cancelCtx.cancel(false, err, cause) + if removeFromParent { + // Remove this timerCtx from its parent cancelCtx's children. + removeChild(c.cancelCtx.Context, c) + } + c.mu.Lock() + if c.timer != nil { + c.timer.Stop() + c.timer = nil + } + c.mu.Unlock() +} + +// WithTimeout returns WithDeadline(parent, time.Now().Add(timeout)). +// +// Canceling this context releases resources associated with it, so code should +// call cancel as soon as the operations running in this [Context] complete: +// +// func slowOperationWithTimeout(ctx context.Context) (Result, error) { +// ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond) +// defer cancel() // releases resources if slowOperation completes before timeout elapses +// return slowOperation(ctx) +// } +func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) { + return WithDeadline(parent, time.Now().Add(timeout)) +} + +// WithTimeoutCause behaves like [WithTimeout] but also sets the cause of the +// returned Context when the timeout expires. The returned [CancelFunc] does +// not set the cause. +func WithTimeoutCause(parent Context, timeout time.Duration, cause error) (Context, CancelFunc) { + return WithDeadlineCause(parent, time.Now().Add(timeout), cause) +} + +// WithValue returns a copy of parent in which the value associated with key is +// val. +// +// Use context Values only for request-scoped data that transits processes and +// APIs, not for passing optional parameters to functions. +// +// The provided key must be comparable and should not be of type +// string or any other built-in type to avoid collisions between +// packages using context. Users of WithValue should define their own +// types for keys. To avoid allocating when assigning to an +// interface{}, context keys often have concrete type +// struct{}. Alternatively, exported context key variables' static +// type should be a pointer or interface. +func WithValue(parent Context, key, val interface{}) Context { + if parent == nil { + panic("cannot create context from nil parent") + } + if key == nil { + panic("nil key") + } + if !reflectlite.TypeOf(key).Comparable() { + panic("key is not comparable") + } + return &valueCtx{parent, key, val} +} + +// A valueCtx carries a key-value pair. It implements Value for that key and +// delegates all other calls to the embedded Context. +type valueCtx struct { + Context + key, val interface{} +} + +// stringify tries a bit to stringify v, without using fmt, since we don't +// want context depending on the unicode tables. This is only used by +// *valueCtx.String(). +func stringify(v interface{}) string { + switch s := v.(type) { + case stringer: + return s.String() + case string: + return s + } + return "<not Stringer>" +} + +func (c *valueCtx) String() string { + return contextName(c.Context) + ".WithValue(type " + + reflectlite.TypeOf(c.key).String() + + ", val " + stringify(c.val) + ")" +} + +func (c *valueCtx) Value(key interface{}) interface{} { + if c.key == key { + return c.val + } + return value(c.Context, key) +} + +func value(c Context, key interface{}) interface{} { + for { + switch ctx := c.(type) { + case *valueCtx: + if key == ctx.key { + return ctx.val + } + c = ctx.Context + case *cancelCtx: + if key == &cancelCtxKey { + return c + } + c = ctx.Context + case withoutCancelCtx: + if key == &cancelCtxKey { + // This implements Cause(ctx) == nil + // when ctx is created using WithoutCancel. + return nil + } + c = ctx.c + case *timerCtx: + if key == &cancelCtxKey { + return &ctx.cancelCtx + } + c = ctx.Context + case backgroundCtx, todoCtx: + return nil + default: + return c.Value(key) + } + } +} diff --git a/internal/ctxt/merge.go b/internal/ctxt/merge.go new file mode 100644 index 0000000000..7604c78e6e --- /dev/null +++ b/internal/ctxt/merge.go @@ -0,0 +1,52 @@ +// package ctxt implements context merging. +package ctxt + +import ( + "context" + "time" +) + +type mergeContext struct { + context.Context + ctx2 context.Context +} + +// Merge returns a context that is cancelled when at least one of the parents +// is cancelled. The returned context also returns the values of ctx1, or ctx2 +// if nil. +func Merge(ctx1, ctx2 context.Context) (context.Context, context.CancelFunc) { + ctx, cancel := WithCancelCause(ctx1) + stop := AfterFunc(ctx2, func() { + cancel(Cause(ctx2)) + }) + + return &mergeContext{ + Context: ctx, + ctx2: ctx2, + }, func() { + stop() + cancel(context.Canceled) + } +} + +// Value returns ctx2's value if ctx's is nil. +func (ctx *mergeContext) Value(key interface{}) interface{} { + if v := ctx.Context.Value(key); v != nil { + return v + } + return ctx.ctx2.Value(key) +} + +// Deadline returns the earlier deadline of the two parents of ctx. +func (ctx *mergeContext) Deadline() (time.Time, bool) { + if d1, ok := ctx.Context.Deadline(); ok { + if d2, ok := ctx.ctx2.Deadline(); ok { + if d1.Before(d2) { + return d1, true + } + return d2, true + } + return d1, ok + } + return ctx.ctx2.Deadline() +} diff --git a/internal/ctxt/merge_test.go b/internal/ctxt/merge_test.go new file mode 100644 index 0000000000..c111ae7a09 --- /dev/null +++ b/internal/ctxt/merge_test.go @@ -0,0 +1,133 @@ +package ctxt_test + +import ( + "context" + "testing" + "time" + + "github.com/gophercloud/gophercloud/internal/ctxt" +) + +func TestMerge(t *testing.T) { + t.Run("returns values from both parents", func(t *testing.T) { + ctx1 := context.WithValue(context.Background(), + "key1", "value1") + + ctx2 := context.WithValue(context.WithValue(context.Background(), + "key1", "this value should be overridden"), + "key2", "value2") + + ctx, cancel := ctxt.Merge(ctx1, ctx2) + defer cancel() + + if v1 := ctx.Value("key1"); v1 != nil { + if s1, ok := v1.(string); ok { + if s1 != "value1" { + t.Errorf("found value for key1 %q, expected %q", s1, "value1") + } + } else { + t.Errorf("key1 is not the expected type string") + } + } else { + t.Errorf("key1 returned nil") + } + + if v2 := ctx.Value("key2"); v2 != nil { + if s2, ok := v2.(string); ok { + if s2 != "value2" { + t.Errorf("found value for key2 %q, expected %q", s2, "value2") + } + } else { + t.Errorf("key2 is not the expected type string") + } + } else { + t.Errorf("key2 returned nil") + } + }) + + t.Run("first parent cancels", func(t *testing.T) { + ctx1, cancel1 := context.WithCancel(context.Background()) + ctx2, cancel2 := context.WithCancel(context.Background()) + defer cancel2() + + ctx, cancel := ctxt.Merge(ctx1, ctx2) + defer cancel() + + if err := ctx.Err(); err != nil { + t.Errorf("context unexpectedly done: %v", err) + } + + cancel1() + time.Sleep(1 * time.Millisecond) + if err := ctx.Err(); err == nil { + t.Errorf("context not done despite parent1 cancelled") + } + }) + + t.Run("second parent cancels", func(t *testing.T) { + ctx1, cancel1 := context.WithCancel(context.Background()) + ctx2, cancel2 := context.WithCancel(context.Background()) + defer cancel1() + + ctx, cancel := ctxt.Merge(ctx1, ctx2) + defer cancel() + + if err := ctx.Err(); err != nil { + t.Errorf("context unexpectedly done: %v", err) + } + + cancel2() + time.Sleep(1 * time.Millisecond) + if err := ctx.Err(); err == nil { + t.Errorf("context not done despite parent2 cancelled") + } + }) + + t.Run("inherits deadline from first parent", func(t *testing.T) { + now := time.Now() + t1 := now.Add(time.Hour) + t2 := t1.Add(time.Second) + + ctx1, cancel1 := context.WithDeadline(context.Background(), t1) + ctx2, cancel2 := context.WithDeadline(context.Background(), t2) + defer cancel1() + defer cancel2() + + ctx, cancel := ctxt.Merge(ctx1, ctx2) + defer cancel() + + if err := ctx.Err(); err != nil { + t.Errorf("context unexpectedly done: %v", err) + } + + if deadline, ok := ctx.Deadline(); ok { + if deadline != t1 { + t.Errorf("expected deadline to be %v, found %v", t1, deadline) + } + } + }) + + t.Run("inherits deadline from second parent", func(t *testing.T) { + now := time.Now() + t2 := now.Add(time.Hour) + t1 := t2.Add(time.Second) + + ctx1, cancel1 := context.WithDeadline(context.Background(), t1) + ctx2, cancel2 := context.WithDeadline(context.Background(), t2) + defer cancel1() + defer cancel2() + + ctx, cancel := ctxt.Merge(ctx1, ctx2) + defer cancel() + + if err := ctx.Err(); err != nil { + t.Errorf("context unexpectedly done: %v", err) + } + + if deadline, ok := ctx.Deadline(); ok { + if deadline != t2 { + t.Errorf("expected deadline to be %v, found %v", t2, deadline) + } + } + }) +} diff --git a/provider_client.go b/provider_client.go index ce291a3ab3..bf43099e0b 100644 --- a/provider_client.go +++ b/provider_client.go @@ -10,6 +10,8 @@ import ( "net/http" "strings" "sync" + + "github.com/gophercloud/gophercloud/internal/ctxt" ) // DefaultUserAgent is the default User-Agent string set in the request header. @@ -88,7 +90,9 @@ type ProviderClient struct { // with the token and reauth func zeroed. Such client can be used to perform reauthorization. Throwaway bool - // Context is the context passed to the HTTP request. + // Context is the context passed to the HTTP request. Values set on the + // per-call context, when available, override values set on this + // context. Context context.Context // Retry backoff func is called when rate limited. @@ -352,15 +356,20 @@ type requestState struct { var applicationJSON = "application/json" -// Request performs an HTTP request using the ProviderClient's current HTTPClient. An authentication -// header will automatically be provided. -func (client *ProviderClient) Request(method, url string, options *RequestOpts) (*http.Response, error) { - return client.doRequest(method, url, options, &requestState{ +// RequestWithContext performs an HTTP request using the ProviderClient's +// current HTTPClient. An authentication header will automatically be provided. +func (client *ProviderClient) RequestWithContext(ctx context.Context, method, url string, options *RequestOpts) (*http.Response, error) { + return client.doRequest(ctx, method, url, options, &requestState{ hasReauthenticated: false, }) } -func (client *ProviderClient) doRequest(method, url string, options *RequestOpts, state *requestState) (*http.Response, error) { +// Request is a compatibility wrapper for Request. +func (client *ProviderClient) Request(method, url string, options *RequestOpts) (*http.Response, error) { + return client.RequestWithContext(context.Background(), method, url, options) +} + +func (client *ProviderClient) doRequest(ctx context.Context, method, url string, options *RequestOpts, state *requestState) (*http.Response, error) { var body io.Reader var contentType *string @@ -389,14 +398,16 @@ func (client *ProviderClient) doRequest(method, url string, options *RequestOpts body = options.RawBody } - // Construct the http.Request. - req, err := http.NewRequest(method, url, body) + if client.Context != nil { + var cancel context.CancelFunc + ctx, cancel = ctxt.Merge(ctx, client.Context) + defer cancel() + } + + req, err := http.NewRequestWithContext(ctx, method, url, body) if err != nil { return nil, err } - if client.Context != nil { - req = req.WithContext(client.Context) - } // Populate the request headers. // Apply options.MoreHeaders and options.OmitHeaders, to give the caller the chance to @@ -432,12 +443,12 @@ func (client *ProviderClient) doRequest(method, url string, options *RequestOpts if client.RetryFunc != nil { var e error state.retries = state.retries + 1 - e = client.RetryFunc(client.Context, method, url, options, err, state.retries) + e = client.RetryFunc(ctx, method, url, options, err, state.retries) if e != nil { return nil, e } - return client.doRequest(method, url, options, state) + return client.doRequest(ctx, method, url, options, state) } return nil, err } @@ -491,7 +502,7 @@ func (client *ProviderClient) doRequest(method, url string, options *RequestOpts } } state.hasReauthenticated = true - resp, err = client.doRequest(method, url, options, state) + resp, err = client.doRequest(ctx, method, url, options, state) if err != nil { switch err.(type) { case *ErrUnexpectedResponseCode: @@ -556,7 +567,7 @@ func (client *ProviderClient) doRequest(method, url string, options *RequestOpts return resp, e } - return client.doRequest(method, url, options, state) + return client.doRequest(ctx, method, url, options, state) } case http.StatusInternalServerError: err = ErrDefault500{respErr} @@ -592,7 +603,7 @@ func (client *ProviderClient) doRequest(method, url string, options *RequestOpts return resp, e } - return client.doRequest(method, url, options, state) + return client.doRequest(ctx, method, url, options, state) } return resp, err @@ -616,7 +627,7 @@ func (client *ProviderClient) doRequest(method, url string, options *RequestOpts return resp, e } - return client.doRequest(method, url, options, state) + return client.doRequest(ctx, method, url, options, state) } return nil, err } diff --git a/service_client.go b/service_client.go index 94a161e340..b8e6fd1a38 100644 --- a/service_client.go +++ b/service_client.go @@ -1,6 +1,7 @@ package gophercloud import ( + "context" "io" "net/http" "strings" @@ -59,58 +60,88 @@ func (client *ServiceClient) initReqOpts(JSONBody interface{}, JSONResponse inte } } -// Get calls `Request` with the "GET" HTTP verb. -func (client *ServiceClient) Get(url string, JSONResponse interface{}, opts *RequestOpts) (*http.Response, error) { +// GetWithContext calls `Request` with the "GET" HTTP verb. +func (client *ServiceClient) GetWithContext(ctx context.Context, url string, JSONResponse interface{}, opts *RequestOpts) (*http.Response, error) { if opts == nil { opts = new(RequestOpts) } client.initReqOpts(nil, JSONResponse, opts) - return client.Request("GET", url, opts) + return client.RequestWithContext(ctx, "GET", url, opts) } -// Post calls `Request` with the "POST" HTTP verb. -func (client *ServiceClient) Post(url string, JSONBody interface{}, JSONResponse interface{}, opts *RequestOpts) (*http.Response, error) { +// Get is a compatibility wrapper for GetWithContext. +func (client *ServiceClient) Get(url string, JSONResponse interface{}, opts *RequestOpts) (*http.Response, error) { + return client.GetWithContext(context.Background(), url, JSONResponse, opts) +} + +// PostWithContext calls `Request` with the "POST" HTTP verb. +func (client *ServiceClient) PostWithContext(ctx context.Context, url string, JSONBody interface{}, JSONResponse interface{}, opts *RequestOpts) (*http.Response, error) { if opts == nil { opts = new(RequestOpts) } client.initReqOpts(JSONBody, JSONResponse, opts) - return client.Request("POST", url, opts) + return client.RequestWithContext(ctx, "POST", url, opts) } -// Put calls `Request` with the "PUT" HTTP verb. -func (client *ServiceClient) Put(url string, JSONBody interface{}, JSONResponse interface{}, opts *RequestOpts) (*http.Response, error) { +// Post is a compatibility wrapper for PostWithContext. +func (client *ServiceClient) Post(url string, JSONBody interface{}, JSONResponse interface{}, opts *RequestOpts) (*http.Response, error) { + return client.PostWithContext(context.Background(), url, JSONBody, JSONResponse, opts) +} + +// PutWithContext calls `Request` with the "PUT" HTTP verb. +func (client *ServiceClient) PutWithContext(ctx context.Context, url string, JSONBody interface{}, JSONResponse interface{}, opts *RequestOpts) (*http.Response, error) { if opts == nil { opts = new(RequestOpts) } client.initReqOpts(JSONBody, JSONResponse, opts) - return client.Request("PUT", url, opts) + return client.RequestWithContext(ctx, "PUT", url, opts) } -// Patch calls `Request` with the "PATCH" HTTP verb. -func (client *ServiceClient) Patch(url string, JSONBody interface{}, JSONResponse interface{}, opts *RequestOpts) (*http.Response, error) { +// Put is a compatibility wrapper for PurWithContext. +func (client *ServiceClient) Put(url string, JSONBody interface{}, JSONResponse interface{}, opts *RequestOpts) (*http.Response, error) { + return client.PutWithContext(context.Background(), url, JSONBody, JSONResponse, opts) +} + +// PatchWithContext calls `Request` with the "PATCH" HTTP verb. +func (client *ServiceClient) PatchWithContext(ctx context.Context, url string, JSONBody interface{}, JSONResponse interface{}, opts *RequestOpts) (*http.Response, error) { if opts == nil { opts = new(RequestOpts) } client.initReqOpts(JSONBody, JSONResponse, opts) - return client.Request("PATCH", url, opts) + return client.RequestWithContext(ctx, "PATCH", url, opts) } -// Delete calls `Request` with the "DELETE" HTTP verb. -func (client *ServiceClient) Delete(url string, opts *RequestOpts) (*http.Response, error) { +// Patch is a compatibility wrapper for PatchWithContext. +func (client *ServiceClient) Patch(url string, JSONBody interface{}, JSONResponse interface{}, opts *RequestOpts) (*http.Response, error) { + return client.PatchWithContext(context.Background(), url, JSONBody, JSONResponse, opts) +} + +// DeleteWithContext calls `Request` with the "DELETE" HTTP verb. +func (client *ServiceClient) DeleteWithContext(ctx context.Context, url string, opts *RequestOpts) (*http.Response, error) { if opts == nil { opts = new(RequestOpts) } client.initReqOpts(nil, nil, opts) - return client.Request("DELETE", url, opts) + return client.RequestWithContext(ctx, "DELETE", url, opts) } -// Head calls `Request` with the "HEAD" HTTP verb. -func (client *ServiceClient) Head(url string, opts *RequestOpts) (*http.Response, error) { +// Delete is a compatibility wrapper for DeleteWithContext. +func (client *ServiceClient) Delete(url string, opts *RequestOpts) (*http.Response, error) { + return client.DeleteWithContext(context.Background(), url, opts) +} + +// HeadWithContext calls `Request` with the "HEAD" HTTP verb. +func (client *ServiceClient) HeadWithContext(ctx context.Context, url string, opts *RequestOpts) (*http.Response, error) { if opts == nil { opts = new(RequestOpts) } client.initReqOpts(nil, nil, opts) - return client.Request("HEAD", url, opts) + return client.RequestWithContext(ctx, "HEAD", url, opts) +} + +// Head is a compatibility wrapper for HeadWithContext. +func (client *ServiceClient) Head(url string, opts *RequestOpts) (*http.Response, error) { + return client.HeadWithContext(context.Background(), url, opts) } func (client *ServiceClient) setMicroversionHeader(opts *RequestOpts) { @@ -133,7 +164,7 @@ func (client *ServiceClient) setMicroversionHeader(opts *RequestOpts) { } // Request carries out the HTTP operation for the service client -func (client *ServiceClient) Request(method, url string, options *RequestOpts) (*http.Response, error) { +func (client *ServiceClient) RequestWithContext(ctx context.Context, method, url string, options *RequestOpts) (*http.Response, error) { if options.MoreHeaders == nil { options.MoreHeaders = make(map[string]string) } @@ -151,7 +182,12 @@ func (client *ServiceClient) Request(method, url string, options *RequestOpts) ( options.MoreHeaders[k] = v } } - return client.ProviderClient.Request(method, url, options) + return client.ProviderClient.RequestWithContext(ctx, method, url, options) +} + +// Request is a compatibility wrapper for RequestWithContext. +func (client *ServiceClient) Request(method, url string, options *RequestOpts) (*http.Response, error) { + return client.RequestWithContext(context.Background(), method, url, options) } // ParseResponse is a helper function to parse http.Response to constituents. From 84073f4050bcb12a55c397de0ffe7b8d405abdd5 Mon Sep 17 00:00:00 2001 From: Pierre-Yves Jourel <pierre-yves.jourel@pyjou.com> Date: Tue, 28 Feb 2023 02:40:23 -0500 Subject: [PATCH 327/360] Add support of Flavor for Octavia --- openstack/loadbalancer/v2/flavors/doc.go | 58 ++++++ openstack/loadbalancer/v2/flavors/requests.go | 112 ++++++++++++ openstack/loadbalancer/v2/flavors/results.go | 79 +++++++++ .../loadbalancer/v2/flavors/testing/doc.go | 1 + .../v2/flavors/testing/fixutres.go | 166 ++++++++++++++++++ .../v2/flavors/testing/requests_test.go | 118 +++++++++++++ openstack/loadbalancer/v2/flavors/urls.go | 16 ++ 7 files changed, 550 insertions(+) create mode 100644 openstack/loadbalancer/v2/flavors/doc.go create mode 100644 openstack/loadbalancer/v2/flavors/requests.go create mode 100644 openstack/loadbalancer/v2/flavors/results.go create mode 100644 openstack/loadbalancer/v2/flavors/testing/doc.go create mode 100644 openstack/loadbalancer/v2/flavors/testing/fixutres.go create mode 100644 openstack/loadbalancer/v2/flavors/testing/requests_test.go create mode 100644 openstack/loadbalancer/v2/flavors/urls.go diff --git a/openstack/loadbalancer/v2/flavors/doc.go b/openstack/loadbalancer/v2/flavors/doc.go new file mode 100644 index 0000000000..cbdd127f55 --- /dev/null +++ b/openstack/loadbalancer/v2/flavors/doc.go @@ -0,0 +1,58 @@ +/* +Package flavors provides information and interaction with Flavors +for the OpenStack Load-balancing service. + +Example to List Flavors + + listOpts := flavors.ListOpts{} + + allPages, err := flavors.List(octaviaClient, listOpts).AllPages() + if err != nil { + panic(err) + } + + allFlavors, err := flavors.ExtractFlavors(allPages) + if err != nil { + panic(err) + } + + for _, flavor := range allFlavors { + fmt.Printf("%+v\n", flavor) + } + +Example to Create a Flavor + + createOpts := flavors.CreateOpts{ + Name: "Flavor name", + Description: "My flavor description", + Enable: true, + FlavorProfileId: "9daa2768-74e7-4d13-bf5d-1b8e0dc239e1", + } + + flavor, err := flavors.Create(octaviaClient, createOpts).Extract() + if err != nil { + panic(err) + } + +Example to Update a Flavor + + flavorID := "d67d56a6-4a86-4688-a282-f46444705c64" + + updateOpts := flavors.UpdateOpts{ + Name: "New name", + } + + flavor, err := flavors.Update(octaviaClient, flavorID, updateOpts).Extract() + if err != nil { + panic(err) + } + +Example to Delete a Flavor + + flavorID := "d67d56a6-4a86-4688-a282-f46444705c64" + err := flavors.Delete(octaviaClient, flavorID).ExtractErr() + if err != nil { + panic(err) + } +*/ +package flavors diff --git a/openstack/loadbalancer/v2/flavors/requests.go b/openstack/loadbalancer/v2/flavors/requests.go new file mode 100644 index 0000000000..77c816a517 --- /dev/null +++ b/openstack/loadbalancer/v2/flavors/requests.go @@ -0,0 +1,112 @@ +package flavors + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// LIST + +type ListOptsBuilder interface { + ToFlavorListQuery() (string, error) +} + +type ListOpts struct { + ID string `q:"id"` + Name string `q:"name"` +} + +func (opts ListOpts) ToFlavorListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := rootURL(c) + if opts != nil { + query, err := opts.ToFlavorListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { + return FlavorPage{pagination.LinkedPageBase{PageResult: r}} + }) +} + +// CREATE + +type CreateOptsBuilder interface { + ToFlavorCreateMap() (map[string]interface{}, error) +} + +type CreateOpts struct { + Name string `json:"name,omitempty"` + Description string `json:"description,omitempty"` + FlavorProfileId string `json:"flavor_profile_id,required:"true""` + Enabled bool `json:"enabled,default:"true""` +} + +func (opts CreateOpts) ToFlavorCreateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "flavor") +} + +func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { + b, err := opts.ToFlavorCreateMap() + if err != nil { + r.Err = err + return + } + resp, err := c.Post(rootURL(c), b, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// GET + +func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := c.Get(resourceURL(c, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// UPDATE + +type UpdateOptsBuilder interface { + ToFlavorUpdateMap() (map[string]interface{}, error) +} + +type UpdateOpts struct { + Name string `json:"name,omitempty"` + Description string `json:"description,omitempty"` + Enabled bool `json:"enabled" default:"true"` +} + +func (opts UpdateOpts) ToFlavorUpdateMap() (map[string]interface{}, error) { + b, err := gophercloud.BuildRequestBody(opts, "flavor") + if err != nil { + return nil, err + } + + return b, nil +} + +func Update(c *gophercloud.ServiceClient, id string, opts UpdateOpts) (r UpdateResult) { + b, err := opts.ToFlavorUpdateMap() + if err != nil { + r.Err = err + return + } + resp, err := c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200, 202}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := c.Delete(resourceURL(c, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} diff --git a/openstack/loadbalancer/v2/flavors/results.go b/openstack/loadbalancer/v2/flavors/results.go new file mode 100644 index 0000000000..46eb171c1a --- /dev/null +++ b/openstack/loadbalancer/v2/flavors/results.go @@ -0,0 +1,79 @@ +package flavors + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +type Flavor struct { + // The unique ID for the Flavor + ID string `json:"id"` + + // Human-readable name for the Flavor. Does not have to be unique. + Name string `json:"name"` + + // Human-readable description for the Flavor. + Description string `json:"description"` + + // Status of the Flavor. + Enabled bool `json:"enabled"` + + // Flavor Profile apply to this Flavor. + FlavorProfileId string `json:"flavor_profile_id"` +} + +type FlavorPage struct { + pagination.LinkedPageBase +} + +func (r FlavorPage) NextPageURL() (string, error) { + var s struct { + Links []gophercloud.Link `json:"flavors_links"` + } + err := r.ExtractInto(&s) + if err != nil { + return "", err + } + return gophercloud.ExtractNextURL(s.Links) +} + +func (r FlavorPage) IsEmpty() (bool, error) { + is, err := ExtractFlavors(r) + return len(is) == 0, err +} + +func ExtractFlavors(r pagination.Page) ([]Flavor, error) { + var s struct { + Flavors []Flavor `json:"flavors"` + } + err := (r.(FlavorPage)).ExtractInto(&s) + return s.Flavors, err +} + +type commonResult struct { + gophercloud.Result +} + +func (r commonResult) Extract() (*Flavor, error) { + var s struct { + Flavor *Flavor `json:"flavor"` + } + err := r.ExtractInto(&s) + return s.Flavor, err +} + +type CreateResult struct { + commonResult +} + +type GetResult struct { + commonResult +} + +type UpdateResult struct { + commonResult +} + +type DeleteResult struct { + gophercloud.ErrResult +} diff --git a/openstack/loadbalancer/v2/flavors/testing/doc.go b/openstack/loadbalancer/v2/flavors/testing/doc.go new file mode 100644 index 0000000000..7603f836a0 --- /dev/null +++ b/openstack/loadbalancer/v2/flavors/testing/doc.go @@ -0,0 +1 @@ +package testing diff --git a/openstack/loadbalancer/v2/flavors/testing/fixutres.go b/openstack/loadbalancer/v2/flavors/testing/fixutres.go new file mode 100644 index 0000000000..65ac9bf8ea --- /dev/null +++ b/openstack/loadbalancer/v2/flavors/testing/fixutres.go @@ -0,0 +1,166 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/flavors" + + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +const FlavorsListBody = ` +{ + "flavors": [ + { + "id": "4c82a610-8c7f-4a72-8cca-42f584e3f6d1", + "name": "Basic", + "description": "A basic standalone Octavia load balancer.", + "enabled": true, + "flavor_profile_id": "bdba88c7-beab-4fc9-a5dd-3635de59185b" + }, + { + "id": "0af3b9cc-9284-44c2-9494-0ec337fa31bb", + "name": "Advance", + "description": "A advance standalone Octavia load balancer.", + "enabled": false, + "flavor_profile_id": "c221abc6-a845-45a0-925c-27110c9d7bdc" + } + ] +} +` + +const SingleFlavorBody = ` +{ + "flavor": { + "id": "5548c807-e6e8-43d7-9ea4-b38d34dd74a0", + "name": "Basic", + "description": "A basic standalone Octavia load balancer.", + "enabled": true, + "flavor_profile_id": "9daa2768-74e7-4d13-bf5d-1b8e0dc239e1" + } +} +` + +const PostUpdateFlavorBody = ` +{ + "flavor": { + "id": "5548c807-e6e8-43d7-9ea4-b38d34dd74a0", + "name": "Basic v2", + "description": "Rename flavor", + "enabled": false, + "flavor_profile_id": "9daa2768-74e7-4d13-bf5d-1b8e0dc239e1" + } +} +` + +var ( + FlavorBasic = flavors.Flavor{ + ID: "4c82a610-8c7f-4a72-8cca-42f584e3f6d1", + Name: "Basic", + Description: "A basic standalone Octavia load balancer.", + Enabled: true, + FlavorProfileId: "bdba88c7-beab-4fc9-a5dd-3635de59185b", + } + + FlavorAdvance = flavors.Flavor{ + ID: "0af3b9cc-9284-44c2-9494-0ec337fa31bb", + Name: "Advance", + Description: "A advance standalone Octavia load balancer.", + Enabled: false, + FlavorProfileId: "c221abc6-a845-45a0-925c-27110c9d7bdc", + } + + FlavorDb = flavors.Flavor{ + ID: "5548c807-e6e8-43d7-9ea4-b38d34dd74a0", + Name: "Basic", + Description: "A basic standalone Octavia load balancer.", + Enabled: true, + FlavorProfileId: "9daa2768-74e7-4d13-bf5d-1b8e0dc239e1", + } + + FlavorUpdated = flavors.Flavor{ + ID: "5548c807-e6e8-43d7-9ea4-b38d34dd74a0", + Name: "Basic v2", + Description: "Rename flavor", + Enabled: false, + FlavorProfileId: "9daa2768-74e7-4d13-bf5d-1b8e0dc239e1", + } +) + +func HandleFlavorListSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v2.0/lbaas/flavors", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + w.Header().Add("Content-Type", "application/json") + r.ParseForm() + marker := r.Form.Get("marker") + switch marker { + case "": + fmt.Fprintf(w, FlavorsListBody) + case "3a0d060b-fcec-4250-9ab6-940b806a12dd": + fmt.Fprintf(w, `{ "flavors": [] }`) + default: + t.Fatalf("/v2.0/lbaas/flavors invoked with unexpected marker=[%s]", marker) + } + }) +} + +func HandleFlavorCreationSuccessfully(t *testing.T, response string) { + th.Mux.HandleFunc("/v2.0/lbaas/flavors", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + th.TestJSONRequest(t, r, `{ + "flavor": { + "name": "Basic", + "description": "A basic standalone Octavia load balancer.", + "enabled": true, + "flavor_profile_id": "9daa2768-74e7-4d13-bf5d-1b8e0dc239e1" + } + }`) + + w.WriteHeader(http.StatusAccepted) + w.Header().Add("Content-Type", "application/json") + fmt.Fprintf(w, response) + }) +} + +func HandleFlavorGetSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v2.0/lbaas/flavors/5548c807-e6e8-43d7-9ea4-b38d34dd74a0", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + th.TestHeader(t, r, "Accept", "application/json") + + fmt.Fprintf(w, SingleFlavorBody) + }) +} + +func HandleFlavorDeletionSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v2.0/lbaas/flavors/5548c807-e6e8-43d7-9ea4-b38d34dd74a0", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "DELETE") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + w.WriteHeader(http.StatusNoContent) + }) +} + +func HandleFlavorUpdateSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v2.0/lbaas/flavors/5548c807-e6e8-43d7-9ea4-b38d34dd74a0", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PUT") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + th.TestHeader(t, r, "Accept", "application/json") + th.TestHeader(t, r, "Content-Type", "application/json") + th.TestJSONRequest(t, r, `{ + "flavor": { + "name": "Basic v2", + "description": "Rename flavor", + "enabled": false + } + }`) + + fmt.Fprintf(w, PostUpdateFlavorBody) + }) +} diff --git a/openstack/loadbalancer/v2/flavors/testing/requests_test.go b/openstack/loadbalancer/v2/flavors/testing/requests_test.go new file mode 100644 index 0000000000..12e6fa94f2 --- /dev/null +++ b/openstack/loadbalancer/v2/flavors/testing/requests_test.go @@ -0,0 +1,118 @@ +package testing + +import ( + "testing" + + "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/flavors" + "github.com/gophercloud/gophercloud/pagination" + + fake "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/testhelper" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestListFlavors(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleFlavorListSuccessfully(t) + + pages := 0 + err := flavors.List(fake.ServiceClient(), flavors.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + pages++ + + actual, err := flavors.ExtractFlavors(page) + if err != nil { + return false, err + } + + if len(actual) != 2 { + t.Fatalf("Expected 2 flavors, got %d", len(actual)) + } + th.CheckDeepEquals(t, FlavorBasic, actual[0]) + th.CheckDeepEquals(t, FlavorAdvance, actual[1]) + + return true, nil + }) + + th.AssertNoErr(t, err) + + if pages != 1 { + t.Errorf("Expected 1 page, saw %d", pages) + } +} + +func TestListAllFlavors(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleFlavorListSuccessfully(t) + + allPages, err := flavors.List(fake.ServiceClient(), flavors.ListOpts{}).AllPages() + th.AssertNoErr(t, err) + actual, err := flavors.ExtractFlavors(allPages) + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, FlavorBasic, actual[0]) + th.CheckDeepEquals(t, FlavorAdvance, actual[1]) +} + +func TestCreateFlavor(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleFlavorCreationSuccessfully(t, SingleFlavorBody) + + actual, err := flavors.Create(fake.ServiceClient(), flavors.CreateOpts{ + Name: "Basic", + Description: "A basic standalone Octavia load balancer.", + Enabled: true, + FlavorProfileId: "9daa2768-74e7-4d13-bf5d-1b8e0dc239e1", + }).Extract() + th.AssertNoErr(t, err) + + th.CheckDeepEquals(t, FlavorDb, *actual) +} + +func TestRequiredCreateOpts(t *testing.T) { + res := flavors.Create(fake.ServiceClient(), flavors.CreateOpts{}) + if res.Err == nil { + t.Fatalf("Expected error, got none") + } +} + +func TestGetFlavor(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleFlavorGetSuccessfully(t) + + client := fake.ServiceClient() + actual, err := flavors.Get(client, "5548c807-e6e8-43d7-9ea4-b38d34dd74a0").Extract() + if err != nil { + t.Fatalf("Unexpected Get error: %v", err) + } + + th.CheckDeepEquals(t, FlavorDb, *actual) +} + +func TestDeleteFlavor(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleFlavorDeletionSuccessfully(t) + + res := flavors.Delete(fake.ServiceClient(), "5548c807-e6e8-43d7-9ea4-b38d34dd74a0") + th.AssertNoErr(t, res.Err) +} + +func TestUpdateFlavor(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleFlavorUpdateSuccessfully(t) + + client := fake.ServiceClient() + actual, err := flavors.Update(client, "5548c807-e6e8-43d7-9ea4-b38d34dd74a0", flavors.UpdateOpts{ + Name: "Basic v2", + Description: "Rename flavor", + Enabled: false, + }).Extract() + if err != nil { + t.Fatalf("Unexpected Update error: %v", err) + } + + th.CheckDeepEquals(t, FlavorUpdated, *actual) +} diff --git a/openstack/loadbalancer/v2/flavors/urls.go b/openstack/loadbalancer/v2/flavors/urls.go new file mode 100644 index 0000000000..5d9a84b1ff --- /dev/null +++ b/openstack/loadbalancer/v2/flavors/urls.go @@ -0,0 +1,16 @@ +package flavors + +import "github.com/gophercloud/gophercloud" + +const ( + rootPath = "lbaas" + resourcePath = "flavors" +) + +func rootURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL(rootPath, resourcePath) +} + +func resourceURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL(rootPath, resourcePath, id) +} From 7dd55c38c5367bae7e1484cff560010ad3ab9227 Mon Sep 17 00:00:00 2001 From: Pierre-Yves Jourel <pierre-yves.jourel@pyjou.com> Date: Tue, 28 Feb 2023 14:44:00 -0500 Subject: [PATCH 328/360] Add support of FlavorProfile for Octavia --- .../loadbalancer/v2/flavorprofiles_test.go | 53 ++++++ .../openstack/loadbalancer/v2/flavors_test.go | 66 ++++++++ .../openstack/loadbalancer/v2/loadbalancer.go | 71 ++++++++ .../loadbalancer/v2/flavorprofiles/doc.go | 57 +++++++ .../v2/flavorprofiles/requests.go | 137 +++++++++++++++ .../loadbalancer/v2/flavorprofiles/results.go | 95 +++++++++++ .../v2/flavorprofiles/testing/doc.go | 1 + .../v2/flavorprofiles/testing/fixtures.go | 157 ++++++++++++++++++ .../flavorprofiles/testing/requests_test.go | 117 +++++++++++++ .../loadbalancer/v2/flavorprofiles/urls.go | 16 ++ openstack/loadbalancer/v2/flavors/requests.go | 63 +++++-- openstack/loadbalancer/v2/flavors/results.go | 19 +++ .../testing/{fixutres.go => fixtures.go} | 2 +- .../v2/flavors/testing/requests_test.go | 2 +- 14 files changed, 837 insertions(+), 19 deletions(-) create mode 100644 internal/acceptance/openstack/loadbalancer/v2/flavorprofiles_test.go create mode 100644 internal/acceptance/openstack/loadbalancer/v2/flavors_test.go create mode 100644 openstack/loadbalancer/v2/flavorprofiles/doc.go create mode 100644 openstack/loadbalancer/v2/flavorprofiles/requests.go create mode 100644 openstack/loadbalancer/v2/flavorprofiles/results.go create mode 100644 openstack/loadbalancer/v2/flavorprofiles/testing/doc.go create mode 100644 openstack/loadbalancer/v2/flavorprofiles/testing/fixtures.go create mode 100644 openstack/loadbalancer/v2/flavorprofiles/testing/requests_test.go create mode 100644 openstack/loadbalancer/v2/flavorprofiles/urls.go rename openstack/loadbalancer/v2/flavors/testing/{fixutres.go => fixtures.go} (99%) diff --git a/internal/acceptance/openstack/loadbalancer/v2/flavorprofiles_test.go b/internal/acceptance/openstack/loadbalancer/v2/flavorprofiles_test.go new file mode 100644 index 0000000000..bde7dbec77 --- /dev/null +++ b/internal/acceptance/openstack/loadbalancer/v2/flavorprofiles_test.go @@ -0,0 +1,53 @@ +//go:build acceptance || networking || loadbalancer || flavorprofiles +// +build acceptance networking loadbalancer flavorprofiles + +package v2 + +import ( + "testing" + + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/flavorprofiles" + + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestFlavorProfilesList(t *testing.T) { + client, err := clients.NewLoadBalancerV2Client() + th.AssertNoErr(t, err) + + allPages, err := flavorprofiles.List(client, nil).AllPages() + th.AssertNoErr(t, err) + + allFlavorProfiles, err := flavorprofiles.ExtractFlavorProfiles(allPages) + th.AssertNoErr(t, err) + + for _, flavorprofile := range allFlavorProfiles { + tools.PrintResource(t, flavorprofile) + } +} + +func TestFlavorProfilesCRUD(t *testing.T) { + lbClient, err := clients.NewLoadBalancerV2Client() + th.AssertNoErr(t, err) + + flavorProfile, err := CreateFlavorProfile(t, lbClient) + th.AssertNoErr(t, err) + defer DeleteFlavorProfile(t, lbClient, flavorProfile) + + tools.PrintResource(t, flavorProfile) + + th.AssertEquals(t, "amphora", flavorProfile.ProviderName) + + flavorProfileUpdateOpts := flavorprofiles.UpdateOpts{ + Name: tools.RandomString("TESTACCTUP-", 8), + } + + flavorProfileUpdated, err := flavorprofiles.Update(lbClient, flavorProfile.ID, flavorProfileUpdateOpts).Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, flavorProfileUpdateOpts.Name, flavorProfileUpdated.Name) + + t.Logf("Successfully updated flavorprofile %s", flavorProfileUpdated.Name) +} diff --git a/internal/acceptance/openstack/loadbalancer/v2/flavors_test.go b/internal/acceptance/openstack/loadbalancer/v2/flavors_test.go new file mode 100644 index 0000000000..f45b4c20a6 --- /dev/null +++ b/internal/acceptance/openstack/loadbalancer/v2/flavors_test.go @@ -0,0 +1,66 @@ +//go:build acceptance || networking || loadbalancer || flavors +// +build acceptance networking loadbalancer flavors + +package v2 + +import ( + "testing" + + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/flavors" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestFlavorsList(t *testing.T) { + client, err := clients.NewLoadBalancerV2Client() + if err != nil { + t.Fatalf("Unable to create a loadbalancer client: %v", err) + } + + allPages, err := flavors.List(client, nil).AllPages() + if err != nil { + t.Fatalf("Unable to list flavors: %v", err) + } + + allFlavors, err := flavors.ExtractFlavors(allPages) + if err != nil { + t.Fatalf("Unable to extract flavors: %v", err) + } + + for _, flavor := range allFlavors { + tools.PrintResource(t, flavor) + } +} + +func TestFlavorsCRUD(t *testing.T) { + lbClient, err := clients.NewLoadBalancerV2Client() + th.AssertNoErr(t, err) + + flavorProfile, err := CreateFlavorProfile(t, lbClient) + th.AssertNoErr(t, err) + defer DeleteFlavorProfile(t, lbClient, flavorProfile) + + tools.PrintResource(t, flavorProfile) + + th.AssertEquals(t, "amphora", flavorProfile.ProviderName) + + flavor, err := CreateFlavor(t, lbClient, flavorProfile) + th.AssertNoErr(t, err) + defer DeleteFlavor(t, lbClient, flavor) + + tools.PrintResource(t, flavor) + + th.AssertEquals(t, flavor.FlavorProfileId, flavorProfile.ID) + + flavorUpdateOpts := flavors.UpdateOpts{ + Name: tools.RandomString("TESTACCTUP-", 8), + } + + flavorUpdated, err := flavors.Update(lbClient, flavor.ID, flavorUpdateOpts).Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, flavorUpdateOpts.Name, flavorUpdated.Name) + + t.Logf("Successfully updated flavor %s", flavorUpdated.Name) +} diff --git a/internal/acceptance/openstack/loadbalancer/v2/loadbalancer.go b/internal/acceptance/openstack/loadbalancer/v2/loadbalancer.go index 72bd638ad0..b5eda52e18 100644 --- a/internal/acceptance/openstack/loadbalancer/v2/loadbalancer.go +++ b/internal/acceptance/openstack/loadbalancer/v2/loadbalancer.go @@ -8,6 +8,8 @@ import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/internal/acceptance/clients" "github.com/gophercloud/gophercloud/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/flavorprofiles" + "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/flavors" "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/l7policies" "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/listeners" "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/loadbalancers" @@ -676,3 +678,72 @@ func WaitForLoadBalancerState(client *gophercloud.ServiceClient, lbID, status st return false, nil }) } + +func CreateFlavorProfile(t *testing.T, client *gophercloud.ServiceClient) (*flavorprofiles.FlavorProfile, error) { + flavorProfileName := tools.RandomString("TESTACCT-", 8) + flavorProfileDriver := "amphora" + flavorProfileData := "{\"loadbalancer_topology\": \"SINGLE\"}" + + createOpts := flavorprofiles.CreateOpts{ + Name: flavorProfileName, + ProviderName: flavorProfileDriver, + FlavorData: flavorProfileData, + } + + flavorProfile, err := flavorprofiles.Create(client, createOpts).Extract() + if err != nil { + return flavorProfile, err + } + + t.Logf("Successfully created flavorprofile %s", flavorProfileName) + + th.AssertEquals(t, flavorProfileName, flavorProfile.Name) + th.AssertEquals(t, flavorProfileDriver, flavorProfile.ProviderName) + th.AssertEquals(t, flavorProfileData, flavorProfile.FlavorData) + + return flavorProfile, nil +} + +func DeleteFlavorProfile(t *testing.T, client *gophercloud.ServiceClient, flavorProfile *flavorprofiles.FlavorProfile) { + err := flavorprofiles.Delete(client, flavorProfile.ID).ExtractErr() + if err != nil { + t.Fatalf("Unable to delete flavorprofile: %v", err) + } + + t.Logf("Successfully deleted flavorprofile %s", flavorProfile.Name) +} + +func CreateFlavor(t *testing.T, client *gophercloud.ServiceClient, flavorProfile *flavorprofiles.FlavorProfile) (*flavors.Flavor, error) { + flavorName := tools.RandomString("TESTACCT-", 8) + description := tools.RandomString("TESTACCT-desc-", 32) + + createOpts := flavors.CreateOpts{ + Name: flavorName, + Description: description, + FlavorProfileId: flavorProfile.ID, + Enabled: true, + } + + flavor, err := flavors.Create(client, createOpts).Extract() + if err != nil { + return flavor, err + } + + t.Logf("Successfully created flavor %s with flavorprofile %s", flavor.Name, flavorProfile.Name) + + th.AssertEquals(t, flavorName, flavor.Name) + th.AssertEquals(t, description, flavor.Description) + th.AssertEquals(t, flavorProfile.ID, flavor.FlavorProfileId) + th.AssertEquals(t, true, flavor.Enabled) + + return flavor, nil +} + +func DeleteFlavor(t *testing.T, client *gophercloud.ServiceClient, flavor *flavors.Flavor) { + err := flavors.Delete(client, flavor.ID).ExtractErr() + if err != nil { + t.Fatalf("Unable to delete flavor: %v", err) + } + + t.Logf("Successfully deleted flavor %s", flavor.Name) +} diff --git a/openstack/loadbalancer/v2/flavorprofiles/doc.go b/openstack/loadbalancer/v2/flavorprofiles/doc.go new file mode 100644 index 0000000000..fcf846f3c3 --- /dev/null +++ b/openstack/loadbalancer/v2/flavorprofiles/doc.go @@ -0,0 +1,57 @@ +/* +Package flavorprofiles provides information and interaction +with FlavorProfiles for the OpenStack Load-balancing service. + +Example to List FlavorProfiles + + listOpts := flavorprofiles.ListOpts{} + + allPages, err := flavorprofiles.List(octaviaClient, listOpts).AllPages() + if err != nil { + panic(err) + } + + allFlavorProfiles, err := flavorprofiles.ExtractFlavorProfiles(allPages) + if err != nil { + panic(err) + } + + for _, flavorProfile := range allFlavorProfiles { + fmt.Printf("%+v\n", flavorProfile) + } + +Example to Create a FlavorProfile + + createOpts := flavorprofiles.CreateOpts{ + Name: "amphora-single", + ProviderName: "amphora", + FlavorData: "{\"loadbalancer_topology\": \"SINGLE\"}", + } + + flavorProfile, err := flavorprofiles.Create(octaviaClient, createOpts).Extract() + if err != nil { + panic(err) + } + +Example to Update a FlavorProfile + + flavorProfileID := "dd6a26af-8085-4047-a62b-3080f4c76521" + + updateOpts := flavorprofiles.UpdateOpts{ + Name: "amphora-single-updated", + } + + flavorProfile, err := flavorprofiles.Update(octaviaClient, flavorProfileID, updateOpts).Extract() + if err != nil { + panic(err) + } + +Example to Delete a FlavorProfile + + flavorProfileID := "dd6a26af-8085-4047-a62b-3080f4c76521" + err := flavorprofiles.Delete(octaviaClient, flavorProfileID).ExtractErr() + if err != nil { + panic(err) + } +*/ +package flavorprofiles diff --git a/openstack/loadbalancer/v2/flavorprofiles/requests.go b/openstack/loadbalancer/v2/flavorprofiles/requests.go new file mode 100644 index 0000000000..886fb5450d --- /dev/null +++ b/openstack/loadbalancer/v2/flavorprofiles/requests.go @@ -0,0 +1,137 @@ +package flavorprofiles + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// ListOptsBuilder allows extensions to add additional parameters to the +// List request. +type ListOptsBuilder interface { + ToFlavorProfileListQuery() (string, error) +} + +// ListOpts allows to manage the output of the request. +type ListOpts struct { + // The fields that you want the server to return + Fields []string `q:"fields"` +} + +// ToFlavorProfileListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToFlavorProfileListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// List returns a Pager which allows you to iterate over a collection of +// FlavorProfiles. It accepts a ListOpts struct, which allows you to filter +// and sort the returned collection for greater efficiency. +func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := rootURL(c) + if opts != nil { + query, err := opts.ToFlavorProfileListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { + return FlavorProfilePage{pagination.LinkedPageBase{PageResult: r}} + }) +} + +// CreateOptsBuilder allows extensions to add additional parameters to the +// Create request. +type CreateOptsBuilder interface { + ToFlavorProfileCreateMap() (map[string]interface{}, error) +} + +// CreateOpts is the common options struct used in this package's Create +// operation. +type CreateOpts struct { + // Human-readable name for the Loadbalancer. Does not have to be unique. + Name string `json:"name" required:"true"` + + // Providing the name of the provider supported by the Octavia installation. + ProviderName string `json:"provider_name" required:"true"` + + // Providing the json string containing the flavor metadata. + FlavorData string `json:"flavor_data" required:"true"` +} + +// ToFlavorProfileCreateMap builds a request body from CreateOpts. +func (opts CreateOpts) ToFlavorProfileCreateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "flavorprofile") +} + +// Create is and operation which add a new FlavorProfile into the database. +// CreateResult will be returned. +func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { + b, err := opts.ToFlavorProfileCreateMap() + if err != nil { + r.Err = err + return + } + resp, err := c.Post(rootURL(c), b, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// Get retrieves a particular FlavorProfile based on its unique ID. +func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := c.Get(resourceURL(c, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// UpdateOptsBuilder allows extensions to add additional parameters to the +// Update request. +type UpdateOptsBuilder interface { + ToFlavorProfileUpdateMap() (map[string]interface{}, error) +} + +// UpdateOpts is the common options struct used in this package's Update +// operation. +type UpdateOpts struct { + // Human-readable name for the Loadbalancer. Does not have to be unique. + Name string `json:"name,omitempty"` + + // Providing the name of the provider supported by the Octavia installation. + ProviderName string `json:"provider_name,omitempty"` + + // Providing the json string containing the flavor metadata. + FlavorData string `json:"flavor_data,omitempty"` +} + +// ToFlavorProfileUpdateMap builds a request body from UpdateOpts. +func (opts UpdateOpts) ToFlavorProfileUpdateMap() (map[string]interface{}, error) { + b, err := gophercloud.BuildRequestBody(opts, "flavorprofile") + if err != nil { + return nil, err + } + + return b, nil +} + +// Update is an operation which modifies the attributes of the specified +// FlavorProfile. +func Update(c *gophercloud.ServiceClient, id string, opts UpdateOpts) (r UpdateResult) { + b, err := opts.ToFlavorProfileUpdateMap() + if err != nil { + r.Err = err + return + } + resp, err := c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200, 202}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// Delete will permanently delete a particular FlavorProfile based on its +// unique ID. +func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := c.Delete(resourceURL(c, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} diff --git a/openstack/loadbalancer/v2/flavorprofiles/results.go b/openstack/loadbalancer/v2/flavorprofiles/results.go new file mode 100644 index 0000000000..a4d7c11c19 --- /dev/null +++ b/openstack/loadbalancer/v2/flavorprofiles/results.go @@ -0,0 +1,95 @@ +package flavorprofiles + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// FlavorProfile provide metadata such as provider, toplogy and instance flavor. +type FlavorProfile struct { + // The unique ID for the Flavor + ID string `json:"id"` + + // Human-readable name for the Flavor. Does not have to be unique. + Name string `json:"name"` + + // Name of the provider + ProviderName string `json:"provider_name"` + + // Flavor data + FlavorData string `json:"flavor_data"` +} + +// FlavorProfilePage is the page returned by a pager when traversing over a +// collection of flavor profiles. +type FlavorProfilePage struct { + pagination.LinkedPageBase +} + +// NextPageURL is invoked when a paginated collection of flavor profiles has +// reached the end of a page and the pager seeks to traverse over a new one. +// In order to do this, it needs to construct the next page's URL. +func (r FlavorProfilePage) NextPageURL() (string, error) { + var s struct { + Links []gophercloud.Link `json:"flavorprofiles_links"` + } + err := r.ExtractInto(&s) + if err != nil { + return "", err + } + return gophercloud.ExtractNextURL(s.Links) +} + +// IsEmpty checks whether a FlavorProfilePage struct is empty. +func (r FlavorProfilePage) IsEmpty() (bool, error) { + is, err := ExtractFlavorProfiles(r) + return len(is) == 0, err +} + +// ExtractFlavorProfiles accepts a Page struct, specifically a FlavorProfilePage +// struct, and extracts the elements into a slice of FlavorProfile structs. In +// other words, a generic collection is mapped into a relevant slice. +func ExtractFlavorProfiles(r pagination.Page) ([]FlavorProfile, error) { + var s struct { + FlavorProfiles []FlavorProfile `json:"flavorprofiles"` + } + err := (r.(FlavorProfilePage)).ExtractInto(&s) + return s.FlavorProfiles, err +} + +type commonResult struct { + gophercloud.Result +} + +// Extract is a function that accepts a result and extracts a flavor profile. +func (r commonResult) Extract() (*FlavorProfile, error) { + var s struct { + FlavorProfile *FlavorProfile `json:"flavorprofile"` + } + err := r.ExtractInto(&s) + return s.FlavorProfile, err +} + +// CreateResult represents the result of a create operation. Call its Extract +// method to interpret it as a FlavorProfile. +type CreateResult struct { + commonResult +} + +// GetResult represents the result of a get operation. Call its Extract +// method to interpret it as a FlavorProfile. +type GetResult struct { + commonResult +} + +// UpdateResult represents the result of an update operation. Call its Extract +// method to interpret it as a FlavorProfile. +type UpdateResult struct { + commonResult +} + +// DeleteResult represents the result of a delete operation. Call its +// ExtractErr method to determine if the request succeeded or failed. +type DeleteResult struct { + gophercloud.ErrResult +} diff --git a/openstack/loadbalancer/v2/flavorprofiles/testing/doc.go b/openstack/loadbalancer/v2/flavorprofiles/testing/doc.go new file mode 100644 index 0000000000..7603f836a0 --- /dev/null +++ b/openstack/loadbalancer/v2/flavorprofiles/testing/doc.go @@ -0,0 +1 @@ +package testing diff --git a/openstack/loadbalancer/v2/flavorprofiles/testing/fixtures.go b/openstack/loadbalancer/v2/flavorprofiles/testing/fixtures.go new file mode 100644 index 0000000000..35c341c576 --- /dev/null +++ b/openstack/loadbalancer/v2/flavorprofiles/testing/fixtures.go @@ -0,0 +1,157 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/flavorprofiles" + + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +const FlavorProfilesListBody = ` +{ + "flavorprofiles": [ + { + "id": "c55d080d-af45-47ee-b48c-4caa5e87724f", + "name": "amphora-single", + "provider_name": "amphora", + "flavor_data": "{\"loadbalancer_topology\": \"SINGLE\"}" + }, + { + "id": "f78d2815-3714-4b6e-91d8-cf821ba01017", + "name": "amphora-act-stdby", + "provider_name": "amphora", + "flavor_data": "{\"loadbalancer_topology\": \"ACTIVE_STANDBY\"}" + } + ] +} +` + +const SingleFlavorProfileBody = ` +{ + "flavorprofile": { + "id": "dcd65be5-f117-4260-ab3d-b32cc5bd1272", + "name": "amphora-test", + "provider_name": "amphora", + "flavor_data": "{\"loadbalancer_topology\": \"ACTIVE_STANDBY\"}" + } +} +` + +const PostUpdateFlavorBody = ` +{ + "flavorprofile": { + "id": "dcd65be5-f117-4260-ab3d-b32cc5bd1272", + "name": "amphora-test-updated", + "provider_name": "amphora", + "flavor_data": "{\"loadbalancer_topology\": \"SINGLE\"}" + } +} +` + +var ( + FlavorProfileSingle = flavorprofiles.FlavorProfile{ + ID: "c55d080d-af45-47ee-b48c-4caa5e87724f", + Name: "amphora-single", + ProviderName: "amphora", + FlavorData: "{\"loadbalancer_topology\": \"SINGLE\"}", + } + + FlavorProfileAct = flavorprofiles.FlavorProfile{ + ID: "f78d2815-3714-4b6e-91d8-cf821ba01017", + Name: "amphora-act-stdby", + ProviderName: "amphora", + FlavorData: "{\"loadbalancer_topology\": \"ACTIVE_STANDBY\"}", + } + + FlavorDb = flavorprofiles.FlavorProfile{ + ID: "dcd65be5-f117-4260-ab3d-b32cc5bd1272", + Name: "amphora-test", + ProviderName: "amphora", + FlavorData: "{\"loadbalancer_topology\": \"ACTIVE_STANDBY\"}", + } + + FlavorUpdated = flavorprofiles.FlavorProfile{ + ID: "dcd65be5-f117-4260-ab3d-b32cc5bd1272", + Name: "amphora-test-updated", + ProviderName: "amphora", + FlavorData: "{\"loadbalancer_topology\": \"SINGLE\"}", + } +) + +func HandleFlavorProfileListSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v2.0/lbaas/flavorprofiles", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + w.Header().Add("Content-Type", "application/json") + r.ParseForm() + marker := r.Form.Get("marker") + switch marker { + case "": + fmt.Fprintf(w, FlavorProfilesListBody) + case "3a0d060b-fcec-4250-9ab6-940b806a12dd": + fmt.Fprintf(w, `{ "flavors": [] }`) + default: + t.Fatalf("/v2.0/lbaas/flavors invoked with unexpected marker=[%s]", marker) + } + }) +} + +func HandleFlavorProfileCreationSuccessfully(t *testing.T, response string) { + th.Mux.HandleFunc("/v2.0/lbaas/flavorprofiles", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + th.TestJSONRequest(t, r, `{ + "flavorprofile": { + "name": "amphora-test", + "provider_name": "amphora", + "flavor_data": "{\"loadbalancer_topology\": \"ACTIVE_STANDBY\"}" + } + }`) + + w.WriteHeader(http.StatusAccepted) + w.Header().Add("Content-Type", "application/json") + fmt.Fprintf(w, response) + }) +} + +func HandleFlavorProfileGetSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v2.0/lbaas/flavorprofiles/dcd65be5-f117-4260-ab3d-b32cc5bd1272", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + th.TestHeader(t, r, "Accept", "application/json") + + fmt.Fprintf(w, SingleFlavorProfileBody) + }) +} + +func HandleFlavorProfileDeletionSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v2.0/lbaas/flavorprofiles/dcd65be5-f117-4260-ab3d-b32cc5bd1272", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "DELETE") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + w.WriteHeader(http.StatusNoContent) + }) +} + +func HandleFlavorProfileUpdateSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v2.0/lbaas/flavorprofiles/dcd65be5-f117-4260-ab3d-b32cc5bd1272", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PUT") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + th.TestHeader(t, r, "Accept", "application/json") + th.TestHeader(t, r, "Content-Type", "application/json") + th.TestJSONRequest(t, r, `{ + "flavorprofile": { + "name": "amphora-test-updated", + "provider_name": "amphora", + "flavor_data": "{\"loadbalancer_topology\": \"SINGLE\"}" + } + }`) + + fmt.Fprintf(w, PostUpdateFlavorBody) + }) +} diff --git a/openstack/loadbalancer/v2/flavorprofiles/testing/requests_test.go b/openstack/loadbalancer/v2/flavorprofiles/testing/requests_test.go new file mode 100644 index 0000000000..1b30e2ff07 --- /dev/null +++ b/openstack/loadbalancer/v2/flavorprofiles/testing/requests_test.go @@ -0,0 +1,117 @@ +package testing + +import ( + "testing" + + "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/flavorprofiles" + "github.com/gophercloud/gophercloud/pagination" + + fake "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/testhelper" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestListFlavorProfiles(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleFlavorProfileListSuccessfully(t) + + pages := 0 + err := flavorprofiles.List(fake.ServiceClient(), flavorprofiles.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + pages++ + + actual, err := flavorprofiles.ExtractFlavorProfiles(page) + if err != nil { + return false, err + } + + if len(actual) != 2 { + t.Fatalf("Expected 2 flavors, got %d", len(actual)) + } + th.CheckDeepEquals(t, FlavorProfileSingle, actual[0]) + th.CheckDeepEquals(t, FlavorProfileAct, actual[1]) + + return true, nil + }) + + th.AssertNoErr(t, err) + + if pages != 1 { + t.Errorf("Expected 1 page, saw %d", pages) + } +} + +func TestListAllFlavorProfiles(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleFlavorProfileListSuccessfully(t) + + allPages, err := flavorprofiles.List(fake.ServiceClient(), flavorprofiles.ListOpts{}).AllPages() + th.AssertNoErr(t, err) + actual, err := flavorprofiles.ExtractFlavorProfiles(allPages) + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, FlavorProfileSingle, actual[0]) + th.CheckDeepEquals(t, FlavorProfileAct, actual[1]) +} + +func TestCreateFlavorProfile(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleFlavorProfileCreationSuccessfully(t, SingleFlavorProfileBody) + + actual, err := flavorprofiles.Create(fake.ServiceClient(), flavorprofiles.CreateOpts{ + Name: "amphora-test", + ProviderName: "amphora", + FlavorData: "{\"loadbalancer_topology\": \"ACTIVE_STANDBY\"}", + }).Extract() + th.AssertNoErr(t, err) + + th.CheckDeepEquals(t, FlavorDb, *actual) +} + +func TestRequiredCreateOpts(t *testing.T) { + res := flavorprofiles.Create(fake.ServiceClient(), flavorprofiles.CreateOpts{}) + if res.Err == nil { + t.Fatalf("Expected error, got none") + } +} + +func TestGetFlavorProfiles(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleFlavorProfileGetSuccessfully(t) + + client := fake.ServiceClient() + actual, err := flavorprofiles.Get(client, "dcd65be5-f117-4260-ab3d-b32cc5bd1272").Extract() + if err != nil { + t.Fatalf("Unexpected Get error: %v", err) + } + + th.CheckDeepEquals(t, FlavorDb, *actual) +} + +func TestDeleteFlavorProfile(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleFlavorProfileDeletionSuccessfully(t) + + res := flavorprofiles.Delete(fake.ServiceClient(), "dcd65be5-f117-4260-ab3d-b32cc5bd1272") + th.AssertNoErr(t, res.Err) +} + +func TestUpdateFlavorProfile(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleFlavorProfileUpdateSuccessfully(t) + + client := fake.ServiceClient() + actual, err := flavorprofiles.Update(client, "dcd65be5-f117-4260-ab3d-b32cc5bd1272", flavorprofiles.UpdateOpts{ + Name: "amphora-test-updated", + ProviderName: "amphora", + FlavorData: "{\"loadbalancer_topology\": \"SINGLE\"}", + }).Extract() + if err != nil { + t.Fatalf("Unexpected Update error: %v", err) + } + + th.CheckDeepEquals(t, FlavorUpdated, *actual) +} diff --git a/openstack/loadbalancer/v2/flavorprofiles/urls.go b/openstack/loadbalancer/v2/flavorprofiles/urls.go new file mode 100644 index 0000000000..6125d77923 --- /dev/null +++ b/openstack/loadbalancer/v2/flavorprofiles/urls.go @@ -0,0 +1,16 @@ +package flavorprofiles + +import "github.com/gophercloud/gophercloud" + +const ( + rootPath = "lbaas" + resourcePath = "flavorprofiles" +) + +func rootURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL(rootPath, resourcePath) +} + +func resourceURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL(rootPath, resourcePath, id) +} diff --git a/openstack/loadbalancer/v2/flavors/requests.go b/openstack/loadbalancer/v2/flavors/requests.go index 77c816a517..0b9509c320 100644 --- a/openstack/loadbalancer/v2/flavors/requests.go +++ b/openstack/loadbalancer/v2/flavors/requests.go @@ -5,22 +5,27 @@ import ( "github.com/gophercloud/gophercloud/pagination" ) -// LIST - +// ListOptsBuilder allows extensions to add additional parameters to the +// List request. type ListOptsBuilder interface { ToFlavorListQuery() (string, error) } +// ListOpts allows to manage the output of the request. type ListOpts struct { - ID string `q:"id"` - Name string `q:"name"` + // The fields that you want the server to return + Fields []string `q:"fields"` } +// ToFlavorListQuery formats a ListOpts into a query string. func (opts ListOpts) ToFlavorListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } +// List returns a Pager which allows you to iterate over a collection of +// Flavor. It accepts a ListOpts struct, which allows you to filter +// and sort the returned collection for greater efficiency. func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := rootURL(c) if opts != nil { @@ -35,23 +40,36 @@ func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { }) } -// CREATE - +// CreateOptsBuilder allows extensions to add additional parameters to the +// Create request. type CreateOptsBuilder interface { ToFlavorCreateMap() (map[string]interface{}, error) } +// CreateOpts is the common options struct used in this package's Create +// operation. type CreateOpts struct { - Name string `json:"name,omitempty"` - Description string `json:"description,omitempty"` - FlavorProfileId string `json:"flavor_profile_id,required:"true""` - Enabled bool `json:"enabled,default:"true""` + // Human-readable name for the Loadbalancer. Does not have to be unique. + Name string `json:"name" required:"true"` + + // Human-readable description for the Flavor. + Description string `json:"description,omitempty"` + + // The ID of the FlavorProfile which give the metadata for the creation of + // a LoadBalancer. + FlavorProfileId string `json:"flavor_profile_id" required:"true"` + + // If the resource is available for use. The default is True. + Enabled bool `json:"enabled,omitempty"` } +// ToFlavorCreateMap builds a request body from CreateOpts. func (opts CreateOpts) ToFlavorCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "flavor") } +// Create is and operation which add a new Flavor into the database. +// CreateResult will be returned. func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToFlavorCreateMap() if err != nil { @@ -63,26 +81,33 @@ func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResul return } -// GET - +// Get retrieves a particular Flavor based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { resp, err := c.Get(resourceURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } -// UPDATE - +// UpdateOptsBuilder allows extensions to add additional parameters to the +// Update request. type UpdateOptsBuilder interface { ToFlavorUpdateMap() (map[string]interface{}, error) } +// UpdateOpts is the common options struct used in this package's Update +// operation. type UpdateOpts struct { - Name string `json:"name,omitempty"` + // Human-readable name for the Loadbalancer. Does not have to be unique. + Name string `json:"name,omitempty"` + + // Human-readable description for the Flavor. Description string `json:"description,omitempty"` - Enabled bool `json:"enabled" default:"true"` + + // If the resource is available for use. + Enabled bool `json:"enabled,omitempty"` } +// ToFlavorUpdateMap builds a request body from UpdateOpts. func (opts UpdateOpts) ToFlavorUpdateMap() (map[string]interface{}, error) { b, err := gophercloud.BuildRequestBody(opts, "flavor") if err != nil { @@ -92,6 +117,8 @@ func (opts UpdateOpts) ToFlavorUpdateMap() (map[string]interface{}, error) { return b, nil } +// Update is an operation which modifies the attributes of the specified +// Flavor. func Update(c *gophercloud.ServiceClient, id string, opts UpdateOpts) (r UpdateResult) { b, err := opts.ToFlavorUpdateMap() if err != nil { @@ -99,12 +126,14 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOpts) (r UpdateR return } resp, err := c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ - OkCodes: []int{200, 202}, + OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } +// Delete will permanently delete a particular Flavor based on its +// unique ID. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { resp, err := c.Delete(resourceURL(c, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/openstack/loadbalancer/v2/flavors/results.go b/openstack/loadbalancer/v2/flavors/results.go index 46eb171c1a..21c517154d 100644 --- a/openstack/loadbalancer/v2/flavors/results.go +++ b/openstack/loadbalancer/v2/flavors/results.go @@ -5,6 +5,7 @@ import ( "github.com/gophercloud/gophercloud/pagination" ) +// Flavor provide specs for the creation of a load balancer. type Flavor struct { // The unique ID for the Flavor ID string `json:"id"` @@ -22,10 +23,15 @@ type Flavor struct { FlavorProfileId string `json:"flavor_profile_id"` } +// FlavorPage is the page returned by a pager when traversing over a +// collection of flavors. type FlavorPage struct { pagination.LinkedPageBase } +// NextPageURL is invoked when a paginated collection of flavors has +// reached the end of a page and the pager seeks to traverse over a new one. +// In order to do this, it needs to construct the next page's URL. func (r FlavorPage) NextPageURL() (string, error) { var s struct { Links []gophercloud.Link `json:"flavors_links"` @@ -37,11 +43,15 @@ func (r FlavorPage) NextPageURL() (string, error) { return gophercloud.ExtractNextURL(s.Links) } +// IsEmpty checks whether a FlavorPage struct is empty. func (r FlavorPage) IsEmpty() (bool, error) { is, err := ExtractFlavors(r) return len(is) == 0, err } +// ExtractFlavors accepts a Page struct, specifically a FlavorPage +// struct, and extracts the elements into a slice of Flavor structs. In +// other words, a generic collection is mapped into a relevant slice. func ExtractFlavors(r pagination.Page) ([]Flavor, error) { var s struct { Flavors []Flavor `json:"flavors"` @@ -54,6 +64,7 @@ type commonResult struct { gophercloud.Result } +// Extract is a function that accepts a result and extracts a flavor. func (r commonResult) Extract() (*Flavor, error) { var s struct { Flavor *Flavor `json:"flavor"` @@ -62,18 +73,26 @@ func (r commonResult) Extract() (*Flavor, error) { return s.Flavor, err } +// CreateResult represents the result of a create operation. Call its Extract +// method to interpret it as a Flavor. type CreateResult struct { commonResult } +// GetResult represents the result of a get operation. Call its Extract +// method to interpret it as a Flavor. type GetResult struct { commonResult } +// UpdateResult represents the result of an update operation. Call its Extract +// method to interpret it as a Flavor. type UpdateResult struct { commonResult } +// DeleteResult represents the result of a delete operation. Call its +// ExtractErr method to determine if the request succeeded or failed. type DeleteResult struct { gophercloud.ErrResult } diff --git a/openstack/loadbalancer/v2/flavors/testing/fixutres.go b/openstack/loadbalancer/v2/flavors/testing/fixtures.go similarity index 99% rename from openstack/loadbalancer/v2/flavors/testing/fixutres.go rename to openstack/loadbalancer/v2/flavors/testing/fixtures.go index 65ac9bf8ea..42e2fc96a0 100644 --- a/openstack/loadbalancer/v2/flavors/testing/fixutres.go +++ b/openstack/loadbalancer/v2/flavors/testing/fixtures.go @@ -157,7 +157,7 @@ func HandleFlavorUpdateSuccessfully(t *testing.T) { "flavor": { "name": "Basic v2", "description": "Rename flavor", - "enabled": false + "enabled": true } }`) diff --git a/openstack/loadbalancer/v2/flavors/testing/requests_test.go b/openstack/loadbalancer/v2/flavors/testing/requests_test.go index 12e6fa94f2..b04f3056f8 100644 --- a/openstack/loadbalancer/v2/flavors/testing/requests_test.go +++ b/openstack/loadbalancer/v2/flavors/testing/requests_test.go @@ -108,7 +108,7 @@ func TestUpdateFlavor(t *testing.T) { actual, err := flavors.Update(client, "5548c807-e6e8-43d7-9ea4-b38d34dd74a0", flavors.UpdateOpts{ Name: "Basic v2", Description: "Rename flavor", - Enabled: false, + Enabled: true, }).Extract() if err != nil { t.Fatalf("Unexpected Update error: %v", err) From bb3f61b4f3bb970b691c1bcc33dc369b93130963 Mon Sep 17 00:00:00 2001 From: Emilien Macchi <emacchi@redhat.com> Date: Fri, 2 Feb 2024 09:33:28 -0500 Subject: [PATCH 329/360] Prepare v1.9.0 --- CHANGELOG.md | 15 +++++++++++++++ provider_client.go | 2 +- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e13f9e17ef..e10078ade6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,18 @@ +## v1.9.0 (2024-02-02) + +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: diff --git a/provider_client.go b/provider_client.go index bf43099e0b..5cb5e150ff 100644 --- a/provider_client.go +++ b/provider_client.go @@ -16,7 +16,7 @@ import ( // DefaultUserAgent is the default User-Agent string set in the request header. const ( - DefaultUserAgent = "gophercloud/v1.8.0" + DefaultUserAgent = "gophercloud/v1.9.0" DefaultMaxBackoffRetries = 60 ) From b0d7555b64cf64871644bdb3e3d8d97b2d65fc6d Mon Sep 17 00:00:00 2001 From: Pierre Prinetti <pierreprinetti@redhat.com> Date: Fri, 2 Feb 2024 16:49:22 +0100 Subject: [PATCH 330/360] tokens: Add WithContext functions --- openstack/identity/v2/tokens/requests.go | 26 +++- .../v3/extensions/ec2tokens/requests.go | 26 +++- .../identity/v3/extensions/oauth1/requests.go | 124 +++++++++++++----- openstack/identity/v3/tokens/requests.go | 50 +++++-- 4 files changed, 166 insertions(+), 60 deletions(-) diff --git a/openstack/identity/v2/tokens/requests.go b/openstack/identity/v2/tokens/requests.go index 84f16c3fc2..67c04fced2 100644 --- a/openstack/identity/v2/tokens/requests.go +++ b/openstack/identity/v2/tokens/requests.go @@ -1,6 +1,10 @@ package tokens -import "github.com/gophercloud/gophercloud" +import ( + "context" + + "github.com/gophercloud/gophercloud" +) // PasswordCredentialsV2 represents the required options to authenticate // with a username and password. @@ -77,17 +81,17 @@ func (opts AuthOptions) ToTokenV2CreateMap() (map[string]interface{}, error) { return b, nil } -// Create authenticates to the identity service and attempts to acquire a Token. +// CreateWithContext authenticates to the identity service and attempts to acquire a Token. // Generally, rather than interact with this call directly, end users should // call openstack.AuthenticatedClient(), which abstracts all of the gory details // about navigating service catalogs and such. -func Create(client *gophercloud.ServiceClient, auth AuthOptionsBuilder) (r CreateResult) { +func CreateWithContext(ctx context.Context, client *gophercloud.ServiceClient, auth AuthOptionsBuilder) (r CreateResult) { b, err := auth.ToTokenV2CreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(CreateURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.PostWithContext(ctx, CreateURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 203}, OmitHeaders: []string{"X-Auth-Token"}, }) @@ -95,11 +99,21 @@ func Create(client *gophercloud.ServiceClient, auth AuthOptionsBuilder) (r Creat return } -// Get validates and retrieves information for user's token. -func Get(client *gophercloud.ServiceClient, token string) (r GetResult) { +// Create is a compatibility wrapper around CreateWithContext +func Create(client *gophercloud.ServiceClient, auth AuthOptionsBuilder) (r CreateResult) { + return CreateWithContext(context.Background(), client, auth) +} + +// GetWithContext validates and retrieves information for user's token. +func GetWithContext(ctx context.Context, client *gophercloud.ServiceClient, token string) (r GetResult) { resp, err := client.Get(GetURL(client, token), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 203}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } + +// Get is a compatibility wrapper around GetWithContext +func Get(client *gophercloud.ServiceClient, token string) (r GetResult) { + return GetWithContext(context.Background(), client, token) +} diff --git a/openstack/identity/v3/extensions/ec2tokens/requests.go b/openstack/identity/v3/extensions/ec2tokens/requests.go index 32ba0e621d..b5b5350c54 100644 --- a/openstack/identity/v3/extensions/ec2tokens/requests.go +++ b/openstack/identity/v3/extensions/ec2tokens/requests.go @@ -1,6 +1,7 @@ package ec2tokens import ( + "context" "crypto/hmac" "crypto/sha1" "crypto/sha256" @@ -287,8 +288,8 @@ func (opts *AuthOptions) ToTokenV3CreateMap(map[string]interface{}) (map[string] return b, nil } -// Create authenticates and either generates a new token from EC2 credentials -func Create(c *gophercloud.ServiceClient, opts tokens.AuthOptionsBuilder) (r tokens.CreateResult) { +// CreateWithContext authenticates and either generates a new token from EC2 credentials +func CreateWithContext(ctx context.Context, c *gophercloud.ServiceClient, opts tokens.AuthOptionsBuilder) (r tokens.CreateResult) { b, err := opts.ToTokenV3CreateMap(nil) if err != nil { r.Err = err @@ -298,7 +299,7 @@ func Create(c *gophercloud.ServiceClient, opts tokens.AuthOptionsBuilder) (r tok // delete "token" element, since it is used in s3tokens deleteBodyElements(b, "token") - resp, err := c.Post(ec2tokensURL(c), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.PostWithContext(ctx, ec2tokensURL(c), b, &r.Body, &gophercloud.RequestOpts{ MoreHeaders: map[string]string{"X-Auth-Token": ""}, OkCodes: []int{200}, }) @@ -306,9 +307,15 @@ func Create(c *gophercloud.ServiceClient, opts tokens.AuthOptionsBuilder) (r tok return } -// ValidateS3Token authenticates an S3 request using EC2 credentials. Doesn't -// generate a new token ID, but returns a tokens.CreateResult. -func ValidateS3Token(c *gophercloud.ServiceClient, opts tokens.AuthOptionsBuilder) (r tokens.CreateResult) { +// Create is a compatibility wrapper around CreateWithContext +func Create(c *gophercloud.ServiceClient, opts tokens.AuthOptionsBuilder) (r tokens.CreateResult) { + return CreateWithContext(context.Background(), c, opts) +} + +// ValidateS3TokenWithContext authenticates an S3 request using EC2 +// credentials. Doesn't generate a new token ID, but returns a +// tokens.CreateResult. +func ValidateS3TokenWithContext(ctx context.Context, c *gophercloud.ServiceClient, opts tokens.AuthOptionsBuilder) (r tokens.CreateResult) { b, err := opts.ToTokenV3CreateMap(nil) if err != nil { r.Err = err @@ -318,7 +325,7 @@ func ValidateS3Token(c *gophercloud.ServiceClient, opts tokens.AuthOptionsBuilde // delete unused element, since it is used in ec2tokens only deleteBodyElements(b, "body_hash", "headers", "host", "params", "path", "verb") - resp, err := c.Post(s3tokensURL(c), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.PostWithContext(ctx, s3tokensURL(c), b, &r.Body, &gophercloud.RequestOpts{ MoreHeaders: map[string]string{"X-Auth-Token": ""}, OkCodes: []int{200}, }) @@ -326,6 +333,11 @@ func ValidateS3Token(c *gophercloud.ServiceClient, opts tokens.AuthOptionsBuilde return } +// ValidateS3Token is a compatibility wrapper around ValidateS3TokenWithContext +func ValidateS3Token(c *gophercloud.ServiceClient, opts tokens.AuthOptionsBuilder) (r tokens.CreateResult) { + return ValidateS3TokenWithContext(context.Background(), c, opts) +} + // The following are small helper functions used to help build the signature. // sumHMAC1 is a func to implement the HMAC SHA1 signature method. diff --git a/openstack/identity/v3/extensions/oauth1/requests.go b/openstack/identity/v3/extensions/oauth1/requests.go index 028b5a45bd..9921056320 100644 --- a/openstack/identity/v3/extensions/oauth1/requests.go +++ b/openstack/identity/v3/extensions/oauth1/requests.go @@ -1,6 +1,7 @@ package oauth1 import ( + "context" "crypto/hmac" "crypto/sha1" "encoding/base64" @@ -133,9 +134,9 @@ func (opts AuthOptions) ToTokenV3CreateMap(map[string]interface{}) (map[string]i return gophercloud.BuildRequestBody(req, "") } -// Create authenticates and either generates a new OpenStack token from an -// OAuth1 token. -func Create(client *gophercloud.ServiceClient, opts tokens.AuthOptionsBuilder) (r tokens.CreateResult) { +// CreateWithContext authenticates and either generates a new OpenStack token +// from an OAuth1 token. +func CreateWithContext(ctx context.Context, client *gophercloud.ServiceClient, opts tokens.AuthOptionsBuilder) (r tokens.CreateResult) { b, err := opts.ToTokenV3CreateMap(nil) if err != nil { r.Err = err @@ -153,7 +154,7 @@ func Create(client *gophercloud.ServiceClient, opts tokens.AuthOptionsBuilder) ( return } - resp, err := client.Post(authURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.PostWithContext(ctx, authURL(client), b, &r.Body, &gophercloud.RequestOpts{ MoreHeaders: h, OkCodes: []int{201}, }) @@ -161,6 +162,11 @@ func Create(client *gophercloud.ServiceClient, opts tokens.AuthOptionsBuilder) ( return } +// Create is a compatibility wrapper around CreateWithContext. +func Create(client *gophercloud.ServiceClient, opts tokens.AuthOptionsBuilder) (r tokens.CreateResult) { + return CreateWithContext(context.Background(), client, opts) +} + // CreateConsumerOptsBuilder allows extensions to add additional parameters to // the CreateConsumer request. type CreateConsumerOptsBuilder interface { @@ -178,27 +184,37 @@ func (opts CreateConsumerOpts) ToOAuth1CreateConsumerMap() (map[string]interface return gophercloud.BuildRequestBody(opts, "consumer") } -// Create creates a new Consumer. -func CreateConsumer(client *gophercloud.ServiceClient, opts CreateConsumerOptsBuilder) (r CreateConsumerResult) { +// CreateConsumerWithContext creates a new Consumer. +func CreateConsumerWithContext(ctx context.Context, client *gophercloud.ServiceClient, opts CreateConsumerOptsBuilder) (r CreateConsumerResult) { b, err := opts.ToOAuth1CreateConsumerMap() if err != nil { r.Err = err return } - resp, err := client.Post(consumersURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.PostWithContext(ctx, consumersURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } -// Delete deletes a Consumer. -func DeleteConsumer(client *gophercloud.ServiceClient, id string) (r DeleteConsumerResult) { - resp, err := client.Delete(consumerURL(client, id), nil) +// CreateConsumer is a compatibility wrapper around CreateConsumerWithContext. +func CreateConsumer(client *gophercloud.ServiceClient, opts CreateConsumerOptsBuilder) (r CreateConsumerResult) { + return CreateConsumerWithContext(context.Background(), client, opts) +} + +// DeleteConsumerWithContext deletes a Consumer. +func DeleteConsumerWithContext(ctx context.Context, client *gophercloud.ServiceClient, id string) (r DeleteConsumerResult) { + resp, err := client.DeleteWithContext(ctx, consumerURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } +// DeleteConsumer is a compatibility wrapper around DeleteConsumerWithContext. +func DeleteConsumer(client *gophercloud.ServiceClient, id string) (r DeleteConsumerResult) { + return DeleteConsumerWithContext(context.Background(), client, id) +} + // List enumerates Consumers. func ListConsumers(client *gophercloud.ServiceClient) pagination.Pager { return pagination.NewPager(client, consumersURL(client), func(r pagination.PageResult) pagination.Page { @@ -206,13 +222,18 @@ func ListConsumers(client *gophercloud.ServiceClient) pagination.Pager { }) } -// GetConsumer retrieves details on a single Consumer by ID. -func GetConsumer(client *gophercloud.ServiceClient, id string) (r GetConsumerResult) { - resp, err := client.Get(consumerURL(client, id), &r.Body, nil) +// GetConsumerWithContext retrieves details on a single Consumer by ID. +func GetConsumerWithContext(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetConsumerResult) { + resp, err := client.GetWithContext(ctx, consumerURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } +// GetConsumer is a compatibility wrapper around GetConsumerWithContext. +func GetConsumer(client *gophercloud.ServiceClient, id string) (r GetConsumerResult) { + return GetConsumerWithContext(context.Background(), client, id) +} + // UpdateConsumerOpts provides options used to update a consumer. type UpdateConsumerOpts struct { // Description is the consumer description. @@ -225,20 +246,25 @@ func (opts UpdateConsumerOpts) ToOAuth1UpdateConsumerMap() (map[string]interface return gophercloud.BuildRequestBody(opts, "consumer") } -// UpdateConsumer updates an existing Consumer. -func UpdateConsumer(client *gophercloud.ServiceClient, id string, opts UpdateConsumerOpts) (r UpdateConsumerResult) { +// UpdateConsumerWithContext updates an existing Consumer. +func UpdateConsumerWithContext(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(consumerURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.PatchWithContext(ctx, consumerURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } +// UpdateConsumer is a compatibility wrapper around UpdateConsumerWithContext. +func UpdateConsumer(client *gophercloud.ServiceClient, id string, opts UpdateConsumerOpts) (r UpdateConsumerResult) { + return UpdateConsumerWithContext(context.Background(), client, id, opts) +} + // RequestTokenOptsBuilder allows extensions to add additional parameters to the // RequestToken request. type RequestTokenOptsBuilder interface { @@ -297,15 +323,15 @@ func (opts RequestTokenOpts) ToOAuth1RequestTokenHeaders(method, u string) (map[ return h, nil } -// RequestToken requests an unauthorized OAuth1 Token. -func RequestToken(client *gophercloud.ServiceClient, opts RequestTokenOptsBuilder) (r TokenResult) { +// RequestTokenWithContext requests an unauthorized OAuth1 Token. +func RequestTokenWithContext(ctx context.Context, client *gophercloud.ServiceClient, opts RequestTokenOptsBuilder) (r TokenResult) { h, err := opts.ToOAuth1RequestTokenHeaders("POST", requestTokenURL(client)) if err != nil { r.Err = err return } - resp, err := client.Post(requestTokenURL(client), nil, nil, &gophercloud.RequestOpts{ + resp, err := client.PostWithContext(ctx, requestTokenURL(client), nil, nil, &gophercloud.RequestOpts{ MoreHeaders: h, OkCodes: []int{201}, KeepResponseBody: true, @@ -323,6 +349,11 @@ func RequestToken(client *gophercloud.ServiceClient, opts RequestTokenOptsBuilde return } +// RequestToken is a compatibility wrapper around RequestTokenWithContext. +func RequestToken(client *gophercloud.ServiceClient, opts RequestTokenOptsBuilder) (r TokenResult) { + return RequestTokenWithContext(context.Background(), client, opts) +} + // AuthorizeTokenOptsBuilder allows extensions to add additional parameters to // the AuthorizeToken request. type AuthorizeTokenOptsBuilder interface { @@ -351,20 +382,25 @@ func (opts AuthorizeTokenOpts) ToOAuth1AuthorizeTokenMap() (map[string]interface return gophercloud.BuildRequestBody(opts, "") } -// AuthorizeToken authorizes an unauthorized consumer token. -func AuthorizeToken(client *gophercloud.ServiceClient, id string, opts AuthorizeTokenOptsBuilder) (r AuthorizeTokenResult) { +// AuthorizeTokenWithContext authorizes an unauthorized consumer token. +func AuthorizeTokenWithContext(ctx context.Context, client *gophercloud.ServiceClient, id string, opts AuthorizeTokenOptsBuilder) (r AuthorizeTokenResult) { b, err := opts.ToOAuth1AuthorizeTokenMap() if err != nil { r.Err = err return } - resp, err := client.Put(authorizeTokenURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.PutWithContext(ctx, authorizeTokenURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } +// AuthorizeToken is a compatibility wrapper around AuthorizeTokenWithContext. +func AuthorizeToken(client *gophercloud.ServiceClient, id string, opts AuthorizeTokenOptsBuilder) (r AuthorizeTokenResult) { + return AuthorizeTokenWithContext(context.Background(), client, id, opts) +} + // CreateAccessTokenOptsBuilder allows extensions to add additional parameters // to the CreateAccessToken request. type CreateAccessTokenOptsBuilder interface { @@ -425,15 +461,15 @@ func (opts CreateAccessTokenOpts) ToOAuth1CreateAccessTokenHeaders(method, u str return headers, nil } -// CreateAccessToken creates a new OAuth1 Access Token -func CreateAccessToken(client *gophercloud.ServiceClient, opts CreateAccessTokenOptsBuilder) (r TokenResult) { +// CreateAccessTokenWithContext creates a new OAuth1 Access Token +func CreateAccessTokenWithContext(ctx context.Context, client *gophercloud.ServiceClient, opts CreateAccessTokenOptsBuilder) (r TokenResult) { h, err := opts.ToOAuth1CreateAccessTokenHeaders("POST", createAccessTokenURL(client)) if err != nil { r.Err = err return } - resp, err := client.Post(createAccessTokenURL(client), nil, nil, &gophercloud.RequestOpts{ + resp, err := client.PostWithContext(ctx, createAccessTokenURL(client), nil, nil, &gophercloud.RequestOpts{ MoreHeaders: h, OkCodes: []int{201}, KeepResponseBody: true, @@ -451,20 +487,35 @@ func CreateAccessToken(client *gophercloud.ServiceClient, opts CreateAccessToken return } -// GetAccessToken retrieves details on a single OAuth1 access token by an ID. -func GetAccessToken(client *gophercloud.ServiceClient, userID string, id string) (r GetAccessTokenResult) { - resp, err := client.Get(userAccessTokenURL(client, userID, id), &r.Body, nil) +// CreateAccessToken is a compatibility wrapper around CreateAccessTokenWithContext. +func CreateAccessToken(client *gophercloud.ServiceClient, opts CreateAccessTokenOptsBuilder) (r TokenResult) { + return CreateAccessTokenWithContext(context.Background(), client, opts) +} + +// GetAccessTokenWithContext retrieves details on a single OAuth1 access token by an ID. +func GetAccessTokenWithContext(ctx context.Context, client *gophercloud.ServiceClient, userID string, id string) (r GetAccessTokenResult) { + resp, err := client.GetWithContext(ctx, userAccessTokenURL(client, userID, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } -// RevokeAccessToken revokes an OAuth1 access token. -func RevokeAccessToken(client *gophercloud.ServiceClient, userID string, id string) (r RevokeAccessTokenResult) { - resp, err := client.Delete(userAccessTokenURL(client, userID, id), nil) +// GetAccessToken is a compatibility wrapper around GetAccessTokenWithContext. +func GetAccessToken(client *gophercloud.ServiceClient, userID string, id string) (r GetAccessTokenResult) { + return GetAccessTokenWithContext(context.Background(), client, userID, id) +} + +// RevokeAccessTokenWithContext revokes an OAuth1 access token. +func RevokeAccessTokenWithContext(ctx context.Context, client *gophercloud.ServiceClient, userID string, id string) (r RevokeAccessTokenResult) { + resp, err := client.DeleteWithContext(ctx, userAccessTokenURL(client, userID, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } +// RevokeAccessToken is a compatibility wrapper around RevokeAccessTokenWithContext. +func RevokeAccessToken(client *gophercloud.ServiceClient, userID string, id string) (r RevokeAccessTokenResult) { + return RevokeAccessTokenWithContext(context.Background(), client, userID, id) +} + // ListAccessTokens enumerates authorized access tokens. func ListAccessTokens(client *gophercloud.ServiceClient, userID string) pagination.Pager { url := userAccessTokensURL(client, userID) @@ -481,14 +532,19 @@ func ListAccessTokenRoles(client *gophercloud.ServiceClient, userID string, id s }) } -// GetAccessTokenRole retrieves details on a single OAuth1 access token role by +// GetAccessTokenRoleWithContext retrieves details on a single OAuth1 access token role by // an ID. -func GetAccessTokenRole(client *gophercloud.ServiceClient, userID string, id string, roleID string) (r GetAccessTokenRoleResult) { - resp, err := client.Get(userAccessTokenRoleURL(client, userID, id, roleID), &r.Body, nil) +func GetAccessTokenRoleWithContext(ctx context.Context, client *gophercloud.ServiceClient, userID string, id string, roleID string) (r GetAccessTokenRoleResult) { + resp, err := client.GetWithContext(ctx, userAccessTokenRoleURL(client, userID, id, roleID), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } +// GetAccessTokenRole is a compatibility wrapper around GetAccessTokenRoleWithContext. +func GetAccessTokenRole(client *gophercloud.ServiceClient, userID string, id string, roleID string) (r GetAccessTokenRoleResult) { + return GetAccessTokenRoleWithContext(context.Background(), client, userID, id, roleID) +} + // The following are small helper functions used to help build the signature. // buildOAuth1QueryString builds a URLEncoded parameters string specific for diff --git a/openstack/identity/v3/tokens/requests.go b/openstack/identity/v3/tokens/requests.go index 1af55d8137..d5b2fe83fd 100644 --- a/openstack/identity/v3/tokens/requests.go +++ b/openstack/identity/v3/tokens/requests.go @@ -1,6 +1,10 @@ package tokens -import "github.com/gophercloud/gophercloud" +import ( + "context" + + "github.com/gophercloud/gophercloud" +) // Scope allows a created token to be limited to a specific domain or project. type Scope struct { @@ -119,9 +123,9 @@ func subjectTokenHeaders(subjectToken string) map[string]string { } } -// Create authenticates and either generates a new token, or changes the Scope +// CreateWithContext authenticates and either generates a new token, or changes the Scope // of an existing token. -func Create(c *gophercloud.ServiceClient, opts AuthOptionsBuilder) (r CreateResult) { +func CreateWithContext(ctx context.Context, c *gophercloud.ServiceClient, opts AuthOptionsBuilder) (r CreateResult) { scope, err := opts.ToTokenV3ScopeMap() if err != nil { r.Err = err @@ -134,16 +138,21 @@ func Create(c *gophercloud.ServiceClient, opts AuthOptionsBuilder) (r CreateResu return } - resp, err := c.Post(tokenURL(c), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.PostWithContext(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(c *gophercloud.ServiceClient, token string) (r GetResult) { - resp, err := c.Get(tokenURL(c), &r.Body, &gophercloud.RequestOpts{ +// Create is a compatibility wrapper around CreateWithContext +func Create(c *gophercloud.ServiceClient, opts AuthOptionsBuilder) (r CreateResult) { + return CreateWithContext(context.Background(), c, opts) +} + +// GetGetWithContext validates and retrieves information about another token. +func GetWithContext(ctx context.Context, c *gophercloud.ServiceClient, token string) (r GetResult) { + resp, err := c.GetWithContext(ctx, tokenURL(c), &r.Body, &gophercloud.RequestOpts{ MoreHeaders: subjectTokenHeaders(token), OkCodes: []int{200, 203}, }) @@ -151,9 +160,14 @@ func Get(c *gophercloud.ServiceClient, token string) (r GetResult) { return } -// Validate determines if a specified token is valid or not. -func Validate(c *gophercloud.ServiceClient, token string) (bool, error) { - resp, err := c.Head(tokenURL(c), &gophercloud.RequestOpts{ +// Get is a compatibility wrapper around GetWithContext +func Get(c *gophercloud.ServiceClient, token string) (r GetResult) { + return GetWithContext(context.Background(), c, token) +} + +// ValidateWithContext determines if a specified token is valid or not. +func ValidateWithContext(ctx context.Context, c *gophercloud.ServiceClient, token string) (bool, error) { + resp, err := c.HeadWithContext(ctx, tokenURL(c), &gophercloud.RequestOpts{ MoreHeaders: subjectTokenHeaders(token), OkCodes: []int{200, 204, 404}, }) @@ -164,11 +178,21 @@ func Validate(c *gophercloud.ServiceClient, token string) (bool, error) { return resp.StatusCode == 200 || resp.StatusCode == 204, nil } -// Revoke immediately makes specified token invalid. -func Revoke(c *gophercloud.ServiceClient, token string) (r RevokeResult) { - resp, err := c.Delete(tokenURL(c), &gophercloud.RequestOpts{ +// Validate is a compatibility wrapper around ValidateWithContext +func Validate(c *gophercloud.ServiceClient, token string) (bool, error) { + return ValidateWithContext(context.Background(), c, token) +} + +// RevokeWithContext immediately makes specified token invalid. +func RevokeWithContext(ctx context.Context, c *gophercloud.ServiceClient, token string) (r RevokeResult) { + resp, err := c.DeleteWithContext(ctx, tokenURL(c), &gophercloud.RequestOpts{ MoreHeaders: subjectTokenHeaders(token), }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } + +// Revoke is a compatibility wrapper around RevokeWithContext +func Revoke(c *gophercloud.ServiceClient, token string) (r RevokeResult) { + return RevokeWithContext(context.Background(), c, token) +} From b8f25bcf0e63e86694f83f688a3fff660fcdd556 Mon Sep 17 00:00:00 2001 From: Pierre Prinetti <pierreprinetti@redhat.com> Date: Fri, 2 Feb 2024 21:00:22 +0100 Subject: [PATCH 331/360] client: Add WithContext functions Allow using a context to cancel and trace authentication calls. --- openstack/client.go | 123 +++++++++++++++++++++++++------------------- 1 file changed, 70 insertions(+), 53 deletions(-) diff --git a/openstack/client.go b/openstack/client.go index 81c907c35b..8c6173a264 100644 --- a/openstack/client.go +++ b/openstack/client.go @@ -1,6 +1,7 @@ package openstack import ( + "context" "fmt" "reflect" "strings" @@ -23,20 +24,18 @@ const ( v3 = "v3" ) -/* -NewClient prepares an unauthenticated ProviderClient instance. -Most users will probably prefer using the AuthenticatedClient function -instead. - -This is useful if you wish to explicitly control the version of the identity -service that's used for authentication explicitly, for example. - -A basic example of using this would be: - - ao, err := openstack.AuthOptionsFromEnv() - provider, err := openstack.NewClient(ao.IdentityEndpoint) - client, err := openstack.NewIdentityV3(provider, gophercloud.EndpointOpts{}) -*/ +// NewClient prepares an unauthenticated ProviderClient instance. +// Most users will probably prefer using the AuthenticatedClient function +// instead. +// +// This is useful if you wish to explicitly control the version of the identity +// service that's used for authentication explicitly, for example. +// +// A basic example of using this would be: +// +// ao, err := openstack.AuthOptionsFromEnv() +// provider, err := openstack.NewClient(ao.IdentityEndpoint) +// client, err := openstack.NewIdentityV3(provider, gophercloud.EndpointOpts{}) func NewClient(endpoint string) (*gophercloud.ProviderClient, error) { base, err := utils.BaseEndpoint(endpoint) if err != nil { @@ -54,42 +53,45 @@ func NewClient(endpoint string) (*gophercloud.ProviderClient, error) { return p, nil } -/* -AuthenticatedClient logs in to an OpenStack cloud found at the identity endpoint -specified by the options, acquires a token, and returns a Provider Client -instance that's ready to operate. - -If the full path to a versioned identity endpoint was specified (example: -http://example.com:5000/v3), that path will be used as the endpoint to query. - -If a versionless endpoint was specified (example: http://example.com:5000/), -the endpoint will be queried to determine which versions of the identity service -are available, then chooses the most recent or most supported version. - -Example: - - ao, err := openstack.AuthOptionsFromEnv() - provider, err := openstack.AuthenticatedClient(ao) - client, err := openstack.NewNetworkV2(provider, gophercloud.EndpointOpts{ - Region: os.Getenv("OS_REGION_NAME"), - }) -*/ -func AuthenticatedClient(options gophercloud.AuthOptions) (*gophercloud.ProviderClient, error) { +// AuthenticatedClientWithContext 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.AuthenticatedClientWithContext(ctx, ao) +// client, err := openstack.NewNetworkV2(provider, gophercloud.EndpointOpts{ +// Region: os.Getenv("OS_REGION_NAME"), +// }) +func AuthenticatedClientWithContext(ctx context.Context, options gophercloud.AuthOptions) (*gophercloud.ProviderClient, error) { client, err := NewClient(options.IdentityEndpoint) if err != nil { return nil, err } - err = Authenticate(client, options) + err = AuthenticateWithContext(ctx, client, options) if err != nil { return nil, err } return client, nil } -// Authenticate or re-authenticate against the most recent identity service -// supported at the provided endpoint. -func Authenticate(client *gophercloud.ProviderClient, options gophercloud.AuthOptions) error { +// AuthenticatedClient is a compatibility wrapper around AuthenticatedClientWithContext +func AuthenticatedClient(options gophercloud.AuthOptions) (*gophercloud.ProviderClient, error) { + return AuthenticatedClientWithContext(context.Background(), options) +} + +// AuthenticateWithContext authenticates or re-authenticates against the most +// recent identity service supported at the provided endpoint. +func AuthenticateWithContext(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/"}, @@ -102,21 +104,31 @@ func Authenticate(client *gophercloud.ProviderClient, options gophercloud.AuthOp switch chosen.ID { case v2: - return v2auth(client, endpoint, options, gophercloud.EndpointOpts{}) + return v2auth(ctx, client, endpoint, options, gophercloud.EndpointOpts{}) case v3: - return v3auth(client, endpoint, &options, gophercloud.EndpointOpts{}) + return v3auth(ctx, client, endpoint, &options, gophercloud.EndpointOpts{}) default: // The switch statement must be out of date from the versions list. return fmt.Errorf("Unrecognized identity version: %s", chosen.ID) } } -// AuthenticateV2 explicitly authenticates against the identity v2 endpoint. +// Authenticate is a compatibility wrapper around AuthenticateWithContext. +func Authenticate(client *gophercloud.ProviderClient, options gophercloud.AuthOptions) error { + return AuthenticateWithContext(context.Background(), client, options) +} + +// AuthenticateV2WithContext explicitly authenticates against the identity v2 endpoint. +func AuthenticateV2WithContext(ctx context.Context, client *gophercloud.ProviderClient, options gophercloud.AuthOptions, eo gophercloud.EndpointOpts) error { + return v2auth(ctx, client, "", options, eo) +} + +// AuthenticateV2 is a compatibility wrapper around AuthenticateV2WithContext. func AuthenticateV2(client *gophercloud.ProviderClient, options gophercloud.AuthOptions, eo gophercloud.EndpointOpts) error { - return v2auth(client, "", options, eo) + return AuthenticateV2WithContext(context.Background(), client, options, eo) } -func v2auth(client *gophercloud.ProviderClient, endpoint string, options gophercloud.AuthOptions, eo gophercloud.EndpointOpts) error { +func v2auth(ctx context.Context, client *gophercloud.ProviderClient, endpoint string, options gophercloud.AuthOptions, eo gophercloud.EndpointOpts) error { v2Client, err := NewIdentityV2(client, eo) if err != nil { return err @@ -136,7 +148,7 @@ func v2auth(client *gophercloud.ProviderClient, endpoint string, options gopherc TokenID: options.TokenID, } - result := tokens2.Create(v2Client, v2Opts) + result := tokens2.CreateWithContext(ctx, v2Client, v2Opts) err = client.SetTokenAndAuthResult(result) if err != nil { @@ -159,7 +171,7 @@ func v2auth(client *gophercloud.ProviderClient, endpoint string, options gopherc tao := options tao.AllowReauth = false client.ReauthFunc = func() error { - err := v2auth(&tac, endpoint, tao, eo) + err := v2auth(ctx, &tac, endpoint, tao, eo) if err != nil { return err } @@ -174,12 +186,17 @@ func v2auth(client *gophercloud.ProviderClient, endpoint string, options gopherc return nil } -// AuthenticateV3 explicitly authenticates against the identity v3 service. +// AuthenticateV3WithContext explicitly authenticates against the identity v3 service. +func AuthenticateV3WithContext(ctx context.Context, client *gophercloud.ProviderClient, options tokens3.AuthOptionsBuilder, eo gophercloud.EndpointOpts) error { + return v3auth(ctx, client, "", options, eo) +} + +// AuthenticateV3 is a compatibility wrapper around AuthenticateV3WithContext func AuthenticateV3(client *gophercloud.ProviderClient, options tokens3.AuthOptionsBuilder, eo gophercloud.EndpointOpts) error { - return v3auth(client, "", options, eo) + return AuthenticateV3WithContext(context.Background(), client, options, eo) } -func v3auth(client *gophercloud.ProviderClient, endpoint string, opts tokens3.AuthOptionsBuilder, eo gophercloud.EndpointOpts) error { +func v3auth(ctx context.Context, client *gophercloud.ProviderClient, endpoint string, opts tokens3.AuthOptionsBuilder, eo gophercloud.EndpointOpts) error { // Override the generated service endpoint with the one returned by the version endpoint. v3Client, err := NewIdentityV3(client, eo) if err != nil { @@ -229,11 +246,11 @@ func v3auth(client *gophercloud.ProviderClient, endpoint string, opts tokens3.Au var result tokens3.CreateResult switch opts.(type) { case *ec2tokens.AuthOptions: - result = ec2tokens.Create(v3Client, opts) + result = ec2tokens.CreateWithContext(ctx, v3Client, opts) case *oauth1.AuthOptions: - result = oauth1.Create(v3Client, opts) + result = oauth1.CreateWithContext(ctx, v3Client, opts) default: - result = tokens3.Create(v3Client, opts) + result = tokens3.CreateWithContext(ctx, v3Client, opts) } err = client.SetTokenAndAuthResult(result) @@ -277,7 +294,7 @@ func v3auth(client *gophercloud.ProviderClient, endpoint string, opts tokens3.Au tao = opts } client.ReauthFunc = func() error { - err := v3auth(&tac, endpoint, tao, eo) + err := v3auth(ctx, &tac, endpoint, tao, eo) if err != nil { return err } From 2dbf414e62b9b5f7d4080ef02119c8112097384d Mon Sep 17 00:00:00 2001 From: Pierre Prinetti <pierreprinetti@redhat.com> Date: Fri, 2 Feb 2024 18:50:56 +0100 Subject: [PATCH 332/360] pager: Add WithContext functions to enable dependant packages to build ListWithContext. --- pagination/http.go | 12 +++++++++--- pagination/pager.go | 34 ++++++++++++++++++++++++---------- 2 files changed, 33 insertions(+), 13 deletions(-) diff --git a/pagination/http.go b/pagination/http.go index 7845cda13b..13b3925232 100644 --- a/pagination/http.go +++ b/pagination/http.go @@ -1,6 +1,7 @@ package pagination import ( + "context" "encoding/json" "io/ioutil" "net/http" @@ -52,11 +53,16 @@ func PageResultFromParsed(resp *http.Response, body interface{}) PageResult { } } -// Request performs an HTTP request and extracts the http.Response from the result. -func Request(client *gophercloud.ServiceClient, headers map[string]string, url string) (*http.Response, error) { - return client.Get(url, nil, &gophercloud.RequestOpts{ +// RequestWithContext performs an HTTP request and extracts the http.Response from the result. +func RequestWithContext(ctx context.Context, client *gophercloud.ServiceClient, headers map[string]string, url string) (*http.Response, error) { + return client.GetWithContext(ctx, url, nil, &gophercloud.RequestOpts{ MoreHeaders: headers, OkCodes: []int{200, 204, 300}, KeepResponseBody: true, }) } + +// Request is a compatibility wrapper around RequestWithContext. +func Request(client *gophercloud.ServiceClient, headers map[string]string, url string) (*http.Response, error) { + return RequestWithContext(context.Background(), client, headers, url) +} diff --git a/pagination/pager.go b/pagination/pager.go index 1dec2703eb..8bc6680804 100644 --- a/pagination/pager.go +++ b/pagination/pager.go @@ -1,6 +1,7 @@ package pagination import ( + "context" "errors" "fmt" "net/http" @@ -69,8 +70,8 @@ func (p Pager) WithPageCreator(createPage func(r PageResult) Page) Pager { } } -func (p Pager) fetchNextPage(url string) (Page, error) { - resp, err := Request(p.client, p.Headers, url) +func (p Pager) fetchNextPage(ctx context.Context, url string) (Page, error) { + resp, err := RequestWithContext(ctx, p.client, p.Headers, url) if err != nil { return nil, err } @@ -83,9 +84,10 @@ func (p Pager) fetchNextPage(url string) (Page, error) { return p.createPage(remembered), nil } -// EachPage iterates over each page returned by a Pager, yielding one at a time to a handler function. -// Return "false" from the handler to prematurely stop iterating. -func (p Pager) EachPage(handler func(Page) (bool, error)) error { +// EachPageWithContext 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) EachPageWithContext(ctx context.Context, handler func(context.Context, Page) (bool, error)) error { if p.Err != nil { return p.Err } @@ -99,7 +101,7 @@ func (p Pager) EachPage(handler func(Page) (bool, error)) error { p.firstPage = nil } else { var err error - currentPage, err = p.fetchNextPage(currentURL) + currentPage, err = p.fetchNextPage(ctx, currentURL) if err != nil { return err } @@ -113,7 +115,7 @@ func (p Pager) EachPage(handler func(Page) (bool, error)) error { return nil } - ok, err := handler(currentPage) + ok, err := handler(ctx, currentPage) if err != nil { return err } @@ -131,9 +133,16 @@ func (p Pager) EachPage(handler func(Page) (bool, error)) error { } } -// AllPages returns all the pages from a `List` operation in a single page, +// EachPage is a compatibility wrapper around EachPageWithContext. +func (p Pager) EachPage(handler func(Page) (bool, error)) error { + return p.EachPageWithContext(context.Background(), func(_ context.Context, p Page) (bool, error) { + return handler(p) + }) +} + +// AllPagesWithContext returns all the pages from a `List` operation in a single page, // allowing the user to retrieve all the pages at once. -func (p Pager) AllPages() (Page, error) { +func (p Pager) AllPagesWithContext(ctx context.Context) (Page, error) { if p.Err != nil { return nil, p.Err } @@ -143,7 +152,7 @@ func (p Pager) AllPages() (Page, error) { var body reflect.Value // Grab a first page to ascertain the page body type. - firstPage, err := p.fetchNextPage(p.initialURL) + firstPage, err := p.fetchNextPage(ctx, p.initialURL) if err != nil { return nil, err } @@ -252,3 +261,8 @@ func (p Pager) AllPages() (Page, error) { // `Extract*` methods will work. return page.Elem().Interface().(Page), err } + +// AllPages is a compatibility wrapper around AllPagesWithContext. +func (p Pager) AllPages() (Page, error) { + return p.AllPagesWithContext(context.Background()) +} From 22c56829f05af19f3ab422ff1b8b9db2000789f9 Mon Sep 17 00:00:00 2001 From: Pierre Prinetti <pierreprinetti@redhat.com> Date: Mon, 29 Jan 2024 10:38:23 +0100 Subject: [PATCH 333/360] Authenticate with a clouds.yaml This commit imports the clouds.yaml parsing code from the utils module, and exposes it in a way that fits the natural Gophercloud authentication flow. Unlike the code from utils, this new `clouds.Parse` function keeps the separation between the ProviderClient (holding the cloud coordinates and a Keystone token) and the ServiceClient (holding specific endpoint configuration). By default, `clouds.Parse` fetches its configuration from the environment, just like the openstack client would do. Example use: ```Go func main() { ctx := context.Background() ao, eo, tlsConfig, err := clouds.Parse() if err != nil { panic(err) } providerClient, err := config.NewProviderClient(ctx, ao, config.WithTLSConfig(tlsConfig)) if err != nil { panic(err) } networkClient, err := openstack.NewNetworkV2(providerClient, eo) if err != nil { panic(err) } } ``` The `clouds.Parse` function accepts several functional options that can modify its behaviour. For example, to use a `clouds.yaml` that exists in a non-standard path: ```Go ao, eo, tlsConfig, err := clouds.Parse(clouds.WithLocations("/my/path/clouds2.yaml")) ``` It is also possible to pass a reader directly. Note that any number of options can be passed, with each of them taking precedence of the previous if there is conflict. ```Go const exampleClouds = `clouds: openstack: auth: auth_url: https://example.com:13000` ao, eo, tlsConfig, err := clouds.Parse( clouds.WithCloudsYAML(strings.NewReader(exampleClouds)), clouds.WithIdentityEndpoint("https://example.com:13001"), clouds.WithCloudName("osp1"), clouds.WithUsername("alice"), ) ``` --- openstack/config/clouds/clouds.go | 271 +++++++++++++++++++++++++ openstack/config/clouds/clouds_test.go | 64 ++++++ openstack/config/clouds/options.go | 188 +++++++++++++++++ openstack/config/clouds/tls.go | 88 ++++++++ openstack/config/clouds/types.go | 203 ++++++++++++++++++ openstack/config/provider_client.go | 70 +++++++ 6 files changed, 884 insertions(+) create mode 100644 openstack/config/clouds/clouds.go create mode 100644 openstack/config/clouds/clouds_test.go create mode 100644 openstack/config/clouds/options.go create mode 100644 openstack/config/clouds/tls.go create mode 100644 openstack/config/clouds/types.go create mode 100644 openstack/config/provider_client.go diff --git a/openstack/config/clouds/clouds.go b/openstack/config/clouds/clouds.go new file mode 100644 index 0000000000..b04416e68d --- /dev/null +++ b/openstack/config/clouds/clouds.go @@ -0,0 +1,271 @@ +// package clouds provides a parser for OpenStack credentials stored in a clouds.yaml file. +// +// Example use: +// +// ctx := context.Background() +// ao, eo, tlsConfig, err := clouds.Parse() +// if err != nil { +// panic(err) +// } +// +// providerClient, err := config.NewProviderClient(ctx, ao, config.WithTLSConfig(tlsConfig)) +// if err != nil { +// panic(err) +// } +// +// networkClient, err := openstack.NewNetworkV2(providerClient, eo) +// if err != nil { +// panic(err) +// } +package clouds + +import ( + "crypto/tls" + "encoding/json" + "errors" + "fmt" + "os" + "path" + "reflect" + + "github.com/gophercloud/gophercloud" + "gopkg.in/yaml.v2" +) + +// Parse fetches a clouds.yaml file from disk and returns the parsed +// credentials. +// +// By default this function mimics the behaviour of python-openstackclient, which is: +// +// - if the environment variable `OS_CLIENT_CONFIG_FILE` is set and points to a +// clouds.yaml, use that location as the only search location for `clouds.yaml` and `secure.yaml`; +// - otherwise, the search locations for `clouds.yaml` and `secure.yaml` are: +// 1. the current working directory (on Linux: `./`) +// 2. the directory `openstack` under the standatd user config location for +// the operating system (on Linux: `${XDG_CONFIG_HOME:-$HOME/.config}/openstack/`) +// 3. on Linux, `/etc/openstack/` +// +// Once `clouds.yaml` is found in a search location, the same location is used to search for `secure.yaml`. +// +// Like in python-openstackclient, relative paths in the `clouds.yaml` section +// `cacert` are interpreted as relative the the current directory, and not to +// the `clouds.yaml` location. +// +// Search locations, as well as individual `clouds.yaml` properties, can be +// overwritten with functional options. +func Parse(opts ...func(*cloudOpts)) (gophercloud.AuthOptions, gophercloud.EndpointOpts, *tls.Config, error) { + options := cloudOpts{ + cloudName: os.Getenv("OS_CLOUD"), + region: os.Getenv("OS_REGION_NAME"), + endpointType: os.Getenv("OS_INTERFACE"), + locations: func() []string { + if path := os.Getenv("OS_CLIENT_CONFIG_FILE"); path != "" { + return []string{path} + } + return nil + }(), + } + + for _, apply := range opts { + apply(&options) + } + + if options.cloudName == "" { + return gophercloud.AuthOptions{}, gophercloud.EndpointOpts{}, nil, fmt.Errorf("the empty string \"\" is not a valid cloud name") + } + + // Set the defaults and open the files for reading. This code only runs + // if no override has been set, because it is fallible. + if options.cloudsyamlReader == nil { + if len(options.locations) < 1 { + cwd, err := os.Getwd() + if err != nil { + return gophercloud.AuthOptions{}, gophercloud.EndpointOpts{}, nil, fmt.Errorf("failed to get the current working directory: %w", err) + } + userConfig, err := os.UserConfigDir() + if err != nil { + return gophercloud.AuthOptions{}, gophercloud.EndpointOpts{}, nil, fmt.Errorf("failed to get the user config directory: %w", err) + } + options.locations = []string{path.Join(cwd, "clouds.yaml"), path.Join(userConfig, "openstack", "clouds.yaml"), path.Join("/etc", "openstack")} + } + + for _, cloudsPath := range options.locations { + var errNotFound *os.PathError + f, err := os.Open(cloudsPath) + if err != nil && !errors.As(err, &errNotFound) { + return gophercloud.AuthOptions{}, gophercloud.EndpointOpts{}, nil, fmt.Errorf("failed to open %q: %w", cloudsPath, err) + } + if err == nil { + defer f.Close() + options.cloudsyamlReader = f + + if options.secureyamlReader == nil { + securePath := path.Join(path.Base(cloudsPath), "secure.yaml") + secureF, err := os.Open(securePath) + if err != nil && !errors.As(err, &errNotFound) { + return gophercloud.AuthOptions{}, gophercloud.EndpointOpts{}, nil, fmt.Errorf("failed to open %q: %w", securePath, err) + } + if err == nil { + defer secureF.Close() + options.secureyamlReader = secureF + } + } + } + } + if options.cloudsyamlReader == nil { + return gophercloud.AuthOptions{}, gophercloud.EndpointOpts{}, nil, fmt.Errorf("clouds file not found. Search locations were: %v", options.locations) + } + } + + // Parse the YAML payloads. + var clouds Clouds + if err := yaml.NewDecoder(options.cloudsyamlReader).Decode(&clouds); err != nil { + return gophercloud.AuthOptions{}, gophercloud.EndpointOpts{}, nil, err + } + + cloud, ok := clouds.Clouds[options.cloudName] + if !ok { + return gophercloud.AuthOptions{}, gophercloud.EndpointOpts{}, nil, fmt.Errorf("cloud %q not found in clouds.yaml", options.cloudName) + } + + if options.secureyamlReader != nil { + var secureClouds Clouds + if err := yaml.NewDecoder(options.secureyamlReader).Decode(&secureClouds); err != nil { + return gophercloud.AuthOptions{}, gophercloud.EndpointOpts{}, nil, fmt.Errorf("failed to parse secure.yaml: %w", err) + } + + if secureCloud, ok := secureClouds.Clouds[options.cloudName]; ok { + // If secureCloud has content and it differs from the cloud entry, + // merge the two together. + if !reflect.DeepEqual((gophercloud.AuthOptions{}), secureClouds) && !reflect.DeepEqual(clouds, secureClouds) { + var err error + cloud, err = mergeClouds(secureCloud, cloud) + if err != nil { + return gophercloud.AuthOptions{}, gophercloud.EndpointOpts{}, nil, fmt.Errorf("unable to merge information from clouds.yaml and secure.yaml") + } + } + } + } + + tlsConfig, err := computeTLSConfig(cloud, options) + if err != nil { + return gophercloud.AuthOptions{}, gophercloud.EndpointOpts{}, nil, fmt.Errorf("unable to compute TLS configuration: %w", err) + } + + endpointType := coalesce(options.endpointType, cloud.EndpointType, cloud.Interface) + + return gophercloud.AuthOptions{ + IdentityEndpoint: coalesce(options.authURL, cloud.AuthInfo.AuthURL), + Username: coalesce(options.username, cloud.AuthInfo.Username), + UserID: coalesce(options.userID, cloud.AuthInfo.UserID), + Password: coalesce(options.password, cloud.AuthInfo.Password), + DomainID: coalesce(options.domainID, cloud.AuthInfo.UserDomainID, cloud.AuthInfo.ProjectDomainID, cloud.AuthInfo.DomainID), + DomainName: coalesce(options.domainName, cloud.AuthInfo.UserDomainName, cloud.AuthInfo.ProjectDomainName, cloud.AuthInfo.DomainName), + TenantID: coalesce(options.projectID, cloud.AuthInfo.ProjectID), + TenantName: coalesce(options.projectName, cloud.AuthInfo.ProjectName), + TokenID: coalesce(options.token, cloud.AuthInfo.Token), + Scope: options.scope, + ApplicationCredentialID: coalesce(options.applicationCredentialID, cloud.AuthInfo.ApplicationCredentialID), + ApplicationCredentialName: coalesce(options.applicationCredentialName, cloud.AuthInfo.ApplicationCredentialName), + ApplicationCredentialSecret: coalesce(options.applicationCredentialSecret, cloud.AuthInfo.ApplicationCredentialSecret), + }, gophercloud.EndpointOpts{ + Region: coalesce(options.region, cloud.RegionName), + Availability: computeAvailability(endpointType), + }, + tlsConfig, + nil +} + +// computeAvailability is a helper method to determine the endpoint type +// requested by the user. +func computeAvailability(endpointType string) gophercloud.Availability { + if endpointType == "internal" || endpointType == "internalURL" { + return gophercloud.AvailabilityInternal + } + if endpointType == "admin" || endpointType == "adminURL" { + return gophercloud.AvailabilityAdmin + } + return gophercloud.AvailabilityPublic +} + +// coalesce returns the first argument that is not the empty string, or the +// empty string. +func coalesce(items ...string) string { + for _, item := range items { + if item != "" { + return item + } + } + return "" +} + +// mergeClouds merges two Clouds recursively (the AuthInfo also gets merged). +// In case both Clouds define a value, the value in the 'override' cloud takes precedence +func mergeClouds(override, cloud Cloud) (Cloud, error) { + overrideJson, err := json.Marshal(override) + if err != nil { + return Cloud{}, err + } + cloudJson, err := json.Marshal(cloud) + if err != nil { + return Cloud{}, err + } + var overrideInterface interface{} + err = json.Unmarshal(overrideJson, &overrideInterface) + if err != nil { + return Cloud{}, err + } + var cloudInterface interface{} + err = json.Unmarshal(cloudJson, &cloudInterface) + if err != nil { + return Cloud{}, err + } + var mergedCloud Cloud + mergedInterface := mergeInterfaces(overrideInterface, cloudInterface) + mergedJson, err := json.Marshal(mergedInterface) + err = json.Unmarshal(mergedJson, &mergedCloud) + if err != nil { + return Cloud{}, err + } + return mergedCloud, nil +} + +// merges two interfaces. In cases where a value is defined for both 'overridingInterface' and +// 'inferiorInterface' the value in 'overridingInterface' will take precedence. +func mergeInterfaces(overridingInterface, inferiorInterface interface{}) interface{} { + switch overriding := overridingInterface.(type) { + case map[string]interface{}: + interfaceMap, ok := inferiorInterface.(map[string]interface{}) + if !ok { + return overriding + } + for k, v := range interfaceMap { + if overridingValue, ok := overriding[k]; ok { + overriding[k] = mergeInterfaces(overridingValue, v) + } else { + overriding[k] = v + } + } + case []interface{}: + list, ok := inferiorInterface.([]interface{}) + if !ok { + return overriding + } + for i := range list { + overriding = append(overriding, list[i]) + } + return overriding + case nil: + // mergeClouds(nil, map[string]interface{...}) -> map[string]interface{...} + v, ok := inferiorInterface.(map[string]interface{}) + if ok { + return v + } + } + // We don't want to override with empty values + if reflect.DeepEqual(overridingInterface, nil) || reflect.DeepEqual(reflect.Zero(reflect.TypeOf(overridingInterface)).Interface(), overridingInterface) { + return inferiorInterface + } else { + return overridingInterface + } +} diff --git a/openstack/config/clouds/clouds_test.go b/openstack/config/clouds/clouds_test.go new file mode 100644 index 0000000000..e25bae0122 --- /dev/null +++ b/openstack/config/clouds/clouds_test.go @@ -0,0 +1,64 @@ +package clouds_test + +import ( + "fmt" + "strings" + + "github.com/gophercloud/gophercloud/openstack/config/clouds" +) + +func ExampleWithCloudName() { + const exampleClouds = `clouds: + openstack: + auth: + auth_url: https://example.com:13000` + + ao, _, _, err := clouds.Parse( + clouds.WithCloudsYAML(strings.NewReader(exampleClouds)), + clouds.WithCloudName("openstack"), + ) + if err != nil { + panic(err) + } + + fmt.Println(ao.IdentityEndpoint) + // Output: https://example.com:13000 +} + +func ExampleWithUserID() { + const exampleClouds = `clouds: + openstack: + auth: + auth_url: https://example.com:13000` + + ao, _, _, err := clouds.Parse( + clouds.WithCloudsYAML(strings.NewReader(exampleClouds)), + clouds.WithCloudName("openstack"), + clouds.WithUsername("Kris"), + ) + if err != nil { + panic(err) + } + + fmt.Println(ao.Username) + // Output: Kris +} + +func ExampleWithRegion() { + const exampleClouds = `clouds: + openstack: + auth: + auth_url: https://example.com:13000` + + _, eo, _, err := clouds.Parse( + clouds.WithCloudsYAML(strings.NewReader(exampleClouds)), + clouds.WithCloudName("openstack"), + clouds.WithRegion("mars"), + ) + if err != nil { + panic(err) + } + + fmt.Println(eo.Region) + // Output: mars +} diff --git a/openstack/config/clouds/options.go b/openstack/config/clouds/options.go new file mode 100644 index 0000000000..30bf6a2ef3 --- /dev/null +++ b/openstack/config/clouds/options.go @@ -0,0 +1,188 @@ +package clouds + +import ( + "io" + + "github.com/gophercloud/gophercloud" +) + +type cloudOpts struct { + cloudName string + locations []string + cloudsyamlReader io.Reader + secureyamlReader io.Reader + + applicationCredentialID string + applicationCredentialName string + applicationCredentialSecret string + authURL string + domainID string + domainName string + endpointType string + password string + projectID string + projectName string + region string + scope *gophercloud.AuthScope + token string + userID string + username string + + caCertPath string + clientCertPath string + clientKeyPath string + insecure *bool +} + +// WithCloudName allows to override the environment variable `OS_CLOUD`. +func WithCloudName(osCloud string) func(*cloudOpts) { + return func(co *cloudOpts) { + co.cloudName = osCloud + } +} + +// WithLocations is a functional option that sets the search locations for the +// clouds.yaml file (and its optional companion secure.yaml). Each location is +// a file path pointing to a possible `clouds.yaml`. +func WithLocations(locations ...string) func(*cloudOpts) { + return func(co *cloudOpts) { + co.locations = locations + } +} + +// WithCloudsYAML is a functional option that lets you pass a clouds.yaml file +// as an io.Reader interface. When this option is passed, FromCloudsYaml will +// not attempt to fetch any file from the file system. To add a secure.yaml, +// use in conjunction with WithSecureYAML. +func WithCloudsYAML(clouds io.Reader) func(*cloudOpts) { + return func(co *cloudOpts) { + co.cloudsyamlReader = clouds + } +} + +// WithSecureYAML is a functional option that lets you pass a secure.yaml file +// as an io.Reader interface, to complement the clouds.yaml that is either +// fetched from the filesystem, or passed with WithCloudsYAML. +func WithSecureYAML(secure io.Reader) func(*cloudOpts) { + return func(co *cloudOpts) { + co.secureyamlReader = secure + } +} + +func WithApplicationCredentialID(applicationCredentialID string) func(*cloudOpts) { + return func(co *cloudOpts) { + co.applicationCredentialID = applicationCredentialID + } +} + +func WithApplicationCredentialName(applicationCredentialName string) func(*cloudOpts) { + return func(co *cloudOpts) { + co.applicationCredentialName = applicationCredentialName + } +} + +func WithApplicationCredentialSecret(applicationCredentialSecret string) func(*cloudOpts) { + return func(co *cloudOpts) { + co.applicationCredentialSecret = applicationCredentialSecret + } +} + +func WithIdentityEndpoint(authURL string) func(*cloudOpts) { + return func(co *cloudOpts) { + co.authURL = authURL + } +} + +func WithDomainID(domainID string) func(*cloudOpts) { + return func(co *cloudOpts) { + co.domainID = domainID + } +} + +func WithDomainName(domainName string) func(*cloudOpts) { + return func(co *cloudOpts) { + co.domainName = domainName + } +} + +// WithRegion allows to override the endpoint type set in clouds.yaml or in the +// environment variable `OS_INTERFACE`. +func WithEndpointType(endpointType string) func(*cloudOpts) { + return func(co *cloudOpts) { + co.endpointType = endpointType + } +} + +func WithPassword(password string) func(*cloudOpts) { + return func(co *cloudOpts) { + co.password = password + } +} + +func WithProjectID(projectID string) func(*cloudOpts) { + return func(co *cloudOpts) { + co.projectID = projectID + } +} + +func WithProjectName(projectName string) func(*cloudOpts) { + return func(co *cloudOpts) { + co.projectName = projectName + } +} + +// WithRegion allows to override the region set in clouds.yaml or in the +// environment variable `OS_REGION_NAME` +func WithRegion(region string) func(*cloudOpts) { + return func(co *cloudOpts) { + co.region = region + } +} + +func WithScope(scope *gophercloud.AuthScope) func(*cloudOpts) { + return func(co *cloudOpts) { + co.scope = scope + } +} + +func WithToken(token string) func(*cloudOpts) { + return func(co *cloudOpts) { + co.token = token + } +} + +func WithUserID(userID string) func(*cloudOpts) { + return func(co *cloudOpts) { + co.userID = userID + } +} + +func WithUsername(username string) func(*cloudOpts) { + return func(co *cloudOpts) { + co.username = username + } +} + +func WithCACertPath(caCertPath string) func(*cloudOpts) { + return func(co *cloudOpts) { + co.caCertPath = caCertPath + } +} + +func WithClientCertPath(clientCertPath string) func(*cloudOpts) { + return func(co *cloudOpts) { + co.clientCertPath = clientCertPath + } +} + +func WithClientKeyPath(clientKeyPath string) func(*cloudOpts) { + return func(co *cloudOpts) { + co.clientKeyPath = clientKeyPath + } +} + +func WithInsecure(insecure bool) func(*cloudOpts) { + return func(co *cloudOpts) { + co.insecure = &insecure + } +} diff --git a/openstack/config/clouds/tls.go b/openstack/config/clouds/tls.go new file mode 100644 index 0000000000..460ede6d04 --- /dev/null +++ b/openstack/config/clouds/tls.go @@ -0,0 +1,88 @@ +package clouds + +import ( + "bytes" + "crypto/tls" + "crypto/x509" + "fmt" + "io/ioutil" + "os" + "path" + "strings" +) + +func computeTLSConfig(cloud Cloud, options cloudOpts) (*tls.Config, error) { + tlsConfig := new(tls.Config) + if caCertPath := coalesce(options.caCertPath, os.Getenv("OS_CACERT"), cloud.CACertFile); caCertPath != "" { + caCertPath, err := resolveTilde(caCertPath) + if err != nil { + return nil, fmt.Errorf("failed to resolve user home directory: %w", err) + } + + caCert, err := ioutil.ReadFile(caCertPath) + if err != nil { + return nil, fmt.Errorf("failed to open the CA cert file: %w", err) + } + + caCertPool := x509.NewCertPool() + if ok := caCertPool.AppendCertsFromPEM(bytes.TrimSpace(caCert)); !ok { + return nil, fmt.Errorf("failed to parse the CA Cert from %q", caCertPath) + } + tlsConfig.RootCAs = caCertPool + } + + tlsConfig.InsecureSkipVerify = func() bool { + if options.insecure != nil { + return *options.insecure + } + if cloud.Verify != nil { + return !*cloud.Verify + } + return false + }() + + if clientCertPath, clientKeyPath := coalesce(options.clientCertPath, os.Getenv("OS_CERT"), cloud.ClientCertFile), coalesce(options.clientKeyPath, os.Getenv("OS_KEY"), cloud.ClientKeyFile); clientCertPath != "" && clientKeyPath != "" { + clientCertPath, err := resolveTilde(clientCertPath) + if err != nil { + return nil, fmt.Errorf("failed to resolve user home directory in client cert path: %w", err) + } + clientKeyPath, err := resolveTilde(clientKeyPath) + if err != nil { + return nil, fmt.Errorf("failed to resolve user home directory in client cert key path: %w", err) + } + + clientCert, err := ioutil.ReadFile(clientCertPath) + if err != nil { + return nil, fmt.Errorf("failed to read the client cert file: %w", err) + } + + clientKey, err := ioutil.ReadFile(clientKeyPath) + if err != nil { + return nil, fmt.Errorf("failed to read the client cert key file: %w", err) + } + + cert, err := tls.X509KeyPair(clientCert, clientKey) + if err != nil { + return nil, err + } + + tlsConfig.Certificates = []tls.Certificate{cert} + } else if clientCertPath != "" && clientKeyPath == "" { + return nil, fmt.Errorf("client cert is set, but client cert key is missing") + } else if clientCertPath == "" && clientKeyPath != "" { + return nil, fmt.Errorf("client cert key is set, but client cert is missing") + } + + return tlsConfig, nil +} + +func resolveTilde(p string) (string, error) { + if after := strings.TrimPrefix(p, "~/"); after != p { + h, err := os.UserHomeDir() + if err != nil { + return "", fmt.Errorf("failed to resolve user home directory: %w", err) + } + return path.Join(h, after), nil + } + return p, nil +} diff --git a/openstack/config/clouds/types.go b/openstack/config/clouds/types.go new file mode 100644 index 0000000000..c1914633ff --- /dev/null +++ b/openstack/config/clouds/types.go @@ -0,0 +1,203 @@ +package clouds + +import "encoding/json" + +// Clouds represents a collection of Cloud entries in a clouds.yaml file. +// The format of clouds.yaml is documented at +// https://docs.openstack.org/os-client-config/latest/user/configuration.html. +type Clouds struct { + Clouds map[string]Cloud `yaml:"clouds" json:"clouds"` +} + +// Cloud represents an entry in a clouds.yaml/public-clouds.yaml/secure.yaml file. +type Cloud struct { + Cloud string `yaml:"cloud,omitempty" json:"cloud,omitempty"` + Profile string `yaml:"profile,omitempty" json:"profile,omitempty"` + AuthInfo *AuthInfo `yaml:"auth,omitempty" json:"auth,omitempty"` + AuthType AuthType `yaml:"auth_type,omitempty" json:"auth_type,omitempty"` + RegionName string `yaml:"region_name,omitempty" json:"region_name,omitempty"` + Regions []Region `yaml:"regions,omitempty" json:"regions,omitempty"` + + // EndpointType and Interface both specify whether to use the public, internal, + // or admin interface of a service. They should be considered synonymous, but + // EndpointType will take precedence when both are specified. + EndpointType string `yaml:"endpoint_type,omitempty" json:"endpoint_type,omitempty"` + Interface string `yaml:"interface,omitempty" json:"interface,omitempty"` + + // API Version overrides. + IdentityAPIVersion string `yaml:"identity_api_version,omitempty" json:"identity_api_version,omitempty"` + VolumeAPIVersion string `yaml:"volume_api_version,omitempty" json:"volume_api_version,omitempty"` + + // Verify whether or not SSL API requests should be verified. + Verify *bool `yaml:"verify,omitempty" json:"verify,omitempty"` + + // CACertFile a path to a CA Cert bundle that can be used as part of + // verifying SSL API requests. + CACertFile string `yaml:"cacert,omitempty" json:"cacert,omitempty"` + + // ClientCertFile a path to a client certificate to use as part of the SSL + // transaction. + ClientCertFile string `yaml:"cert,omitempty" json:"cert,omitempty"` + + // ClientKeyFile a path to a client key to use as part of the SSL + // transaction. + ClientKeyFile string `yaml:"key,omitempty" json:"key,omitempty"` +} + +// AuthInfo represents the auth section of a cloud entry or +// auth options entered explicitly in ClientOpts. +type AuthInfo struct { + // AuthURL is the keystone/identity endpoint URL. + AuthURL string `yaml:"auth_url,omitempty" json:"auth_url,omitempty"` + + // Token is a pre-generated authentication token. + Token string `yaml:"token,omitempty" json:"token,omitempty"` + + // Username is the username of the user. + Username string `yaml:"username,omitempty" json:"username,omitempty"` + + // UserID is the unique ID of a user. + UserID string `yaml:"user_id,omitempty" json:"user_id,omitempty"` + + // Password is the password of the user. + Password string `yaml:"password,omitempty" json:"password,omitempty"` + + // Application Credential ID to login with. + ApplicationCredentialID string `yaml:"application_credential_id,omitempty" json:"application_credential_id,omitempty"` + + // Application Credential name to login with. + ApplicationCredentialName string `yaml:"application_credential_name,omitempty" json:"application_credential_name,omitempty"` + + // Application Credential secret to login with. + ApplicationCredentialSecret string `yaml:"application_credential_secret,omitempty" json:"application_credential_secret,omitempty"` + + // SystemScope is a system information to scope to. + SystemScope string `yaml:"system_scope,omitempty" json:"system_scope,omitempty"` + + // ProjectName is the common/human-readable name of a project. + // Users can be scoped to a project. + // ProjectName on its own is not enough to ensure a unique scope. It must + // also be combined with either a ProjectDomainName or ProjectDomainID. + // ProjectName cannot be combined with ProjectID in a scope. + ProjectName string `yaml:"project_name,omitempty" json:"project_name,omitempty"` + + // ProjectID is the unique ID of a project. + // It can be used to scope a user to a specific project. + ProjectID string `yaml:"project_id,omitempty" json:"project_id,omitempty"` + + // UserDomainName is the name of the domain where a user resides. + // It is used to identify the source domain of a user. + UserDomainName string `yaml:"user_domain_name,omitempty" json:"user_domain_name,omitempty"` + + // UserDomainID is the unique ID of the domain where a user resides. + // It is used to identify the source domain of a user. + UserDomainID string `yaml:"user_domain_id,omitempty" json:"user_domain_id,omitempty"` + + // ProjectDomainName is the name of the domain where a project resides. + // It is used to identify the source domain of a project. + // ProjectDomainName can be used in addition to a ProjectName when scoping + // a user to a specific project. + ProjectDomainName string `yaml:"project_domain_name,omitempty" json:"project_domain_name,omitempty"` + + // ProjectDomainID is the name of the domain where a project resides. + // It is used to identify the source domain of a project. + // ProjectDomainID can be used in addition to a ProjectName when scoping + // a user to a specific project. + ProjectDomainID string `yaml:"project_domain_id,omitempty" json:"project_domain_id,omitempty"` + + // DomainName is the name of a domain which can be used to identify the + // source domain of either a user or a project. + // If UserDomainName and ProjectDomainName are not specified, then DomainName + // is used as a default choice. + // It can also be used be used to specify a domain-only scope. + DomainName string `yaml:"domain_name,omitempty" json:"domain_name,omitempty"` + + // DomainID is the unique ID of a domain which can be used to identify the + // source domain of eitehr a user or a project. + // If UserDomainID and ProjectDomainID are not specified, then DomainID is + // used as a default choice. + // It can also be used be used to specify a domain-only scope. + DomainID string `yaml:"domain_id,omitempty" json:"domain_id,omitempty"` + + // DefaultDomain is the domain ID to fall back on if no other domain has + // been specified and a domain is required for scope. + DefaultDomain string `yaml:"default_domain,omitempty" json:"default_domain,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 `yaml:"allow_reauth,omitempty" json:"allow_reauth,omitempty"` +} + +// Region represents a region included as part of cloud in clouds.yaml +// According to Python-based openstacksdk, this can be either a struct (as defined) +// or a plain string. Custom unmarshallers handle both cases. +type Region struct { + Name string `yaml:"name,omitempty" json:"name,omitempty"` + Values Cloud `yaml:"values,omitempty" json:"values,omitempty"` +} + +// UnmarshalJSON handles either a plain string acting as the Name property or +// a struct, mimicking the Python-based openstacksdk. +func (r *Region) UnmarshalJSON(data []byte) error { + var name string + if err := json.Unmarshal(data, &name); err == nil { + r.Name = name + return nil + } + + type region Region + var tmp region + if err := json.Unmarshal(data, &tmp); err != nil { + return err + } + r.Name = tmp.Name + r.Values = tmp.Values + + return nil +} + +// UnmarshalYAML handles either a plain string acting as the Name property or +// a struct, mimicking the Python-based openstacksdk. +func (r *Region) UnmarshalYAML(unmarshal func(interface{}) error) error { + var name string + if err := unmarshal(&name); err == nil { + r.Name = name + return nil + } + + type region Region + var tmp region + if err := unmarshal(&tmp); err != nil { + return err + } + r.Name = tmp.Name + r.Values = tmp.Values + + return nil +} + +// AuthType respresents a valid method of authentication. +type AuthType string + +const ( + // AuthPassword defines an unknown version of the password + AuthPassword AuthType = "password" + // AuthToken defined an unknown version of the token + AuthToken AuthType = "token" + + // AuthV2Password defines version 2 of the password + AuthV2Password AuthType = "v2password" + // AuthV2Token defines version 2 of the token + AuthV2Token AuthType = "v2token" + + // AuthV3Password defines version 3 of the password + AuthV3Password AuthType = "v3password" + // AuthV3Token defines version 3 of the token + AuthV3Token AuthType = "v3token" + + // AuthV3ApplicationCredential defines version 3 of the application credential + AuthV3ApplicationCredential AuthType = "v3applicationcredential" +) diff --git a/openstack/config/provider_client.go b/openstack/config/provider_client.go new file mode 100644 index 0000000000..a26b3511bd --- /dev/null +++ b/openstack/config/provider_client.go @@ -0,0 +1,70 @@ +package config + +import ( + "context" + "crypto/tls" + "net/http" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack" +) + +type options struct { + httpClient http.Client + tlsConfig *tls.Config +} + +// WithHTTPClient enables passing a custom http.Client to be used in the +// ProviderClient for authentication and for any further call, for example when +// using a ServiceClient derived from this ProviderClient. +func WithHTTPClient(httpClient http.Client) func(*options) { + return func(o *options) { + o.httpClient = httpClient + } +} + +// WithTLSConfig replaces the Transport of the default HTTP client (or of the +// HTTP client passed with WithHTTPClient) with a RoundTripper containing the +// given TLS config. +func WithTLSConfig(tlsConfig *tls.Config) func(*options) { + return func(o *options) { + o.tlsConfig = tlsConfig + } +} + +// NewProviderClient 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. +func NewProviderClient(ctx context.Context, authOptions gophercloud.AuthOptions, opts ...func(*options)) (*gophercloud.ProviderClient, error) { + var options options + for _, apply := range opts { + apply(&options) + } + + client, err := openstack.NewClient(authOptions.IdentityEndpoint) + if err != nil { + return nil, err + } + + if options.tlsConfig != nil { + transport := http.DefaultTransport.(*http.Transport).Clone() + transport.TLSClientConfig = options.tlsConfig + options.httpClient.Transport = transport + } + client.HTTPClient = options.httpClient + + err = openstack.AuthenticateWithContext(ctx, client, authOptions) + if err != nil { + return nil, err + } + return client, nil +} From af248d5a5dc495630277e7760a7d58eadb2301b4 Mon Sep 17 00:00:00 2001 From: Vladimir Ermakov <vooon341@gmail.com> Date: Tue, 13 Feb 2024 11:59:09 +0100 Subject: [PATCH 334/360] clouds: fix default /etc/openstack/clouds.yaml selection That fix following error returned by Parse(): ``` Failed to parse clouds.yaml: yaml: input error: read /etc/openstack: is a directory ``` Signed-off-by: Vladimir Ermakov <vooon341@gmail.com> --- openstack/config/clouds/clouds.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openstack/config/clouds/clouds.go b/openstack/config/clouds/clouds.go index b04416e68d..d16801adfa 100644 --- a/openstack/config/clouds/clouds.go +++ b/openstack/config/clouds/clouds.go @@ -86,7 +86,7 @@ func Parse(opts ...func(*cloudOpts)) (gophercloud.AuthOptions, gophercloud.Endpo if err != nil { return gophercloud.AuthOptions{}, gophercloud.EndpointOpts{}, nil, fmt.Errorf("failed to get the user config directory: %w", err) } - options.locations = []string{path.Join(cwd, "clouds.yaml"), path.Join(userConfig, "openstack", "clouds.yaml"), path.Join("/etc", "openstack")} + options.locations = []string{path.Join(cwd, "clouds.yaml"), path.Join(userConfig, "openstack", "clouds.yaml"), path.Join("/etc", "openstack", "clouds.yaml")} } for _, cloudsPath := range options.locations { From 018a0795e6715ceea69113e8d484c6fb42bcea1f Mon Sep 17 00:00:00 2001 From: Vladimir Ermakov <vooon341@gmail.com> Date: Tue, 13 Feb 2024 12:11:32 +0100 Subject: [PATCH 335/360] clouds: export ParseOption to allow conditionally compose With* opts Fix #2913 Signed-off-by: Vladimir Ermakov <vooon341@gmail.com> --- openstack/config/clouds/clouds.go | 2 +- openstack/config/clouds/options.go | 49 ++++++++++++++++-------------- 2 files changed, 27 insertions(+), 24 deletions(-) diff --git a/openstack/config/clouds/clouds.go b/openstack/config/clouds/clouds.go index d16801adfa..54f3e8774b 100644 --- a/openstack/config/clouds/clouds.go +++ b/openstack/config/clouds/clouds.go @@ -53,7 +53,7 @@ import ( // // Search locations, as well as individual `clouds.yaml` properties, can be // overwritten with functional options. -func Parse(opts ...func(*cloudOpts)) (gophercloud.AuthOptions, gophercloud.EndpointOpts, *tls.Config, error) { +func Parse(opts ...ParseOption) (gophercloud.AuthOptions, gophercloud.EndpointOpts, *tls.Config, error) { options := cloudOpts{ cloudName: os.Getenv("OS_CLOUD"), region: os.Getenv("OS_REGION_NAME"), diff --git a/openstack/config/clouds/options.go b/openstack/config/clouds/options.go index 30bf6a2ef3..459c217555 100644 --- a/openstack/config/clouds/options.go +++ b/openstack/config/clouds/options.go @@ -34,8 +34,11 @@ type cloudOpts struct { insecure *bool } +// ParseOption one of parse configuration returned by With* modifier +type ParseOption = func(*cloudOpts) + // WithCloudName allows to override the environment variable `OS_CLOUD`. -func WithCloudName(osCloud string) func(*cloudOpts) { +func WithCloudName(osCloud string) ParseOption { return func(co *cloudOpts) { co.cloudName = osCloud } @@ -44,7 +47,7 @@ func WithCloudName(osCloud string) func(*cloudOpts) { // WithLocations is a functional option that sets the search locations for the // clouds.yaml file (and its optional companion secure.yaml). Each location is // a file path pointing to a possible `clouds.yaml`. -func WithLocations(locations ...string) func(*cloudOpts) { +func WithLocations(locations ...string) ParseOption { return func(co *cloudOpts) { co.locations = locations } @@ -54,7 +57,7 @@ func WithLocations(locations ...string) func(*cloudOpts) { // as an io.Reader interface. When this option is passed, FromCloudsYaml will // not attempt to fetch any file from the file system. To add a secure.yaml, // use in conjunction with WithSecureYAML. -func WithCloudsYAML(clouds io.Reader) func(*cloudOpts) { +func WithCloudsYAML(clouds io.Reader) ParseOption { return func(co *cloudOpts) { co.cloudsyamlReader = clouds } @@ -63,43 +66,43 @@ func WithCloudsYAML(clouds io.Reader) func(*cloudOpts) { // WithSecureYAML is a functional option that lets you pass a secure.yaml file // as an io.Reader interface, to complement the clouds.yaml that is either // fetched from the filesystem, or passed with WithCloudsYAML. -func WithSecureYAML(secure io.Reader) func(*cloudOpts) { +func WithSecureYAML(secure io.Reader) ParseOption { return func(co *cloudOpts) { co.secureyamlReader = secure } } -func WithApplicationCredentialID(applicationCredentialID string) func(*cloudOpts) { +func WithApplicationCredentialID(applicationCredentialID string) ParseOption { return func(co *cloudOpts) { co.applicationCredentialID = applicationCredentialID } } -func WithApplicationCredentialName(applicationCredentialName string) func(*cloudOpts) { +func WithApplicationCredentialName(applicationCredentialName string) ParseOption { return func(co *cloudOpts) { co.applicationCredentialName = applicationCredentialName } } -func WithApplicationCredentialSecret(applicationCredentialSecret string) func(*cloudOpts) { +func WithApplicationCredentialSecret(applicationCredentialSecret string) ParseOption { return func(co *cloudOpts) { co.applicationCredentialSecret = applicationCredentialSecret } } -func WithIdentityEndpoint(authURL string) func(*cloudOpts) { +func WithIdentityEndpoint(authURL string) ParseOption { return func(co *cloudOpts) { co.authURL = authURL } } -func WithDomainID(domainID string) func(*cloudOpts) { +func WithDomainID(domainID string) ParseOption { return func(co *cloudOpts) { co.domainID = domainID } } -func WithDomainName(domainName string) func(*cloudOpts) { +func WithDomainName(domainName string) ParseOption { return func(co *cloudOpts) { co.domainName = domainName } @@ -107,25 +110,25 @@ func WithDomainName(domainName string) func(*cloudOpts) { // WithRegion allows to override the endpoint type set in clouds.yaml or in the // environment variable `OS_INTERFACE`. -func WithEndpointType(endpointType string) func(*cloudOpts) { +func WithEndpointType(endpointType string) ParseOption { return func(co *cloudOpts) { co.endpointType = endpointType } } -func WithPassword(password string) func(*cloudOpts) { +func WithPassword(password string) ParseOption { return func(co *cloudOpts) { co.password = password } } -func WithProjectID(projectID string) func(*cloudOpts) { +func WithProjectID(projectID string) ParseOption { return func(co *cloudOpts) { co.projectID = projectID } } -func WithProjectName(projectName string) func(*cloudOpts) { +func WithProjectName(projectName string) ParseOption { return func(co *cloudOpts) { co.projectName = projectName } @@ -133,55 +136,55 @@ func WithProjectName(projectName string) func(*cloudOpts) { // WithRegion allows to override the region set in clouds.yaml or in the // environment variable `OS_REGION_NAME` -func WithRegion(region string) func(*cloudOpts) { +func WithRegion(region string) ParseOption { return func(co *cloudOpts) { co.region = region } } -func WithScope(scope *gophercloud.AuthScope) func(*cloudOpts) { +func WithScope(scope *gophercloud.AuthScope) ParseOption { return func(co *cloudOpts) { co.scope = scope } } -func WithToken(token string) func(*cloudOpts) { +func WithToken(token string) ParseOption { return func(co *cloudOpts) { co.token = token } } -func WithUserID(userID string) func(*cloudOpts) { +func WithUserID(userID string) ParseOption { return func(co *cloudOpts) { co.userID = userID } } -func WithUsername(username string) func(*cloudOpts) { +func WithUsername(username string) ParseOption { return func(co *cloudOpts) { co.username = username } } -func WithCACertPath(caCertPath string) func(*cloudOpts) { +func WithCACertPath(caCertPath string) ParseOption { return func(co *cloudOpts) { co.caCertPath = caCertPath } } -func WithClientCertPath(clientCertPath string) func(*cloudOpts) { +func WithClientCertPath(clientCertPath string) ParseOption { return func(co *cloudOpts) { co.clientCertPath = clientCertPath } } -func WithClientKeyPath(clientKeyPath string) func(*cloudOpts) { +func WithClientKeyPath(clientKeyPath string) ParseOption { return func(co *cloudOpts) { co.clientKeyPath = clientKeyPath } } -func WithInsecure(insecure bool) func(*cloudOpts) { +func WithInsecure(insecure bool) ParseOption { return func(co *cloudOpts) { co.insecure = &insecure } From ac7c64d7b35c129ed8e786aa1b3abc87f7726290 Mon Sep 17 00:00:00 2001 From: Pierre Prinetti <pierreprinetti@redhat.com> Date: Mon, 12 Feb 2024 12:54:38 +0100 Subject: [PATCH 336/360] build(deps): bump EmilienM/devstack-action from 0.11 to 0.14 Also start using the commit hash of the release, rather than the tag. --- .github/workflows/functional-baremetal.yaml | 2 +- .github/workflows/functional-basic.yaml | 2 +- .github/workflows/functional-blockstorage.yaml | 2 +- .github/workflows/functional-clustering.yaml | 2 +- .github/workflows/functional-compute.yaml | 2 +- .github/workflows/functional-containerinfra.yaml | 2 +- .github/workflows/functional-dns.yaml | 2 +- .github/workflows/functional-fwaas_v2.yaml | 2 +- .github/workflows/functional-identity.yaml | 2 +- .github/workflows/functional-imageservice.yaml | 2 +- .github/workflows/functional-keymanager.yaml | 2 +- .github/workflows/functional-loadbalancer.yaml | 2 +- .github/workflows/functional-messaging.yaml | 2 +- .github/workflows/functional-networking.yaml | 2 +- .github/workflows/functional-objectstorage.yaml | 2 +- .github/workflows/functional-orchestration.yaml | 2 +- .github/workflows/functional-placement.yaml | 2 +- .github/workflows/functional-sharedfilesystems.yaml | 2 +- 18 files changed, 18 insertions(+), 18 deletions(-) diff --git a/.github/workflows/functional-baremetal.yaml b/.github/workflows/functional-baremetal.yaml index f66da92a26..831a672442 100644 --- a/.github/workflows/functional-baremetal.yaml +++ b/.github/workflows/functional-baremetal.yaml @@ -39,7 +39,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.11 + uses: EmilienM/devstack-action@c41f86d8df58b53c55f070207b6dfce656788cfd with: branch: ${{ matrix.openstack_version }} conf_overrides: | diff --git a/.github/workflows/functional-basic.yaml b/.github/workflows/functional-basic.yaml index a679a0f27e..c7331f42b8 100644 --- a/.github/workflows/functional-basic.yaml +++ b/.github/workflows/functional-basic.yaml @@ -42,7 +42,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.11 + uses: EmilienM/devstack-action@c41f86d8df58b53c55f070207b6dfce656788cfd with: branch: ${{ matrix.openstack_version }} enabled_services: 's-account,s-container,s-object,s-proxy' diff --git a/.github/workflows/functional-blockstorage.yaml b/.github/workflows/functional-blockstorage.yaml index b0b5f759ec..721a9a5f6e 100644 --- a/.github/workflows/functional-blockstorage.yaml +++ b/.github/workflows/functional-blockstorage.yaml @@ -39,7 +39,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.11 + uses: EmilienM/devstack-action@c41f86d8df58b53c55f070207b6dfce656788cfd with: branch: ${{ matrix.openstack_version }} conf_overrides: | diff --git a/.github/workflows/functional-clustering.yaml b/.github/workflows/functional-clustering.yaml index c7d7a53c65..529c6e3833 100644 --- a/.github/workflows/functional-clustering.yaml +++ b/.github/workflows/functional-clustering.yaml @@ -39,7 +39,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.11 + uses: EmilienM/devstack-action@c41f86d8df58b53c55f070207b6dfce656788cfd with: branch: ${{ matrix.openstack_version }} conf_overrides: | diff --git a/.github/workflows/functional-compute.yaml b/.github/workflows/functional-compute.yaml index 6cf62990ff..cc6261b984 100644 --- a/.github/workflows/functional-compute.yaml +++ b/.github/workflows/functional-compute.yaml @@ -39,7 +39,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.11 + uses: EmilienM/devstack-action@c41f86d8df58b53c55f070207b6dfce656788cfd with: branch: ${{ matrix.openstack_version }} conf_overrides: | diff --git a/.github/workflows/functional-containerinfra.yaml b/.github/workflows/functional-containerinfra.yaml index 9ed76d8026..335fcbbe51 100644 --- a/.github/workflows/functional-containerinfra.yaml +++ b/.github/workflows/functional-containerinfra.yaml @@ -63,7 +63,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.11 + uses: EmilienM/devstack-action@c41f86d8df58b53c55f070207b6dfce656788cfd with: branch: ${{ matrix.openstack_version }} conf_overrides: | diff --git a/.github/workflows/functional-dns.yaml b/.github/workflows/functional-dns.yaml index 43177088c7..d01af4bb93 100644 --- a/.github/workflows/functional-dns.yaml +++ b/.github/workflows/functional-dns.yaml @@ -40,7 +40,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.11 + uses: EmilienM/devstack-action@c41f86d8df58b53c55f070207b6dfce656788cfd with: branch: ${{ matrix.openstack_version }} conf_overrides: | diff --git a/.github/workflows/functional-fwaas_v2.yaml b/.github/workflows/functional-fwaas_v2.yaml index 020163ce27..e2b2f5c04b 100644 --- a/.github/workflows/functional-fwaas_v2.yaml +++ b/.github/workflows/functional-fwaas_v2.yaml @@ -27,7 +27,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.11 + uses: EmilienM/devstack-action@c41f86d8df58b53c55f070207b6dfce656788cfd with: branch: ${{ matrix.openstack_version }} conf_overrides: | diff --git a/.github/workflows/functional-identity.yaml b/.github/workflows/functional-identity.yaml index a62ddacf78..650f1fc0b8 100644 --- a/.github/workflows/functional-identity.yaml +++ b/.github/workflows/functional-identity.yaml @@ -39,7 +39,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.11 + uses: EmilienM/devstack-action@c41f86d8df58b53c55f070207b6dfce656788cfd with: branch: ${{ matrix.openstack_version }} - name: Checkout go diff --git a/.github/workflows/functional-imageservice.yaml b/.github/workflows/functional-imageservice.yaml index 9dec83dd54..a99cdb175c 100644 --- a/.github/workflows/functional-imageservice.yaml +++ b/.github/workflows/functional-imageservice.yaml @@ -39,7 +39,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.11 + uses: EmilienM/devstack-action@c41f86d8df58b53c55f070207b6dfce656788cfd with: branch: ${{ matrix.openstack_version }} - name: Checkout go diff --git a/.github/workflows/functional-keymanager.yaml b/.github/workflows/functional-keymanager.yaml index e0fb3db115..22668b3b7d 100644 --- a/.github/workflows/functional-keymanager.yaml +++ b/.github/workflows/functional-keymanager.yaml @@ -39,7 +39,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.11 + uses: EmilienM/devstack-action@c41f86d8df58b53c55f070207b6dfce656788cfd with: branch: ${{ matrix.openstack_version }} conf_overrides: | diff --git a/.github/workflows/functional-loadbalancer.yaml b/.github/workflows/functional-loadbalancer.yaml index 8dc42208cf..45ab11b657 100644 --- a/.github/workflows/functional-loadbalancer.yaml +++ b/.github/workflows/functional-loadbalancer.yaml @@ -39,7 +39,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.11 + uses: EmilienM/devstack-action@c41f86d8df58b53c55f070207b6dfce656788cfd with: branch: ${{ matrix.openstack_version }} conf_overrides: | diff --git a/.github/workflows/functional-messaging.yaml b/.github/workflows/functional-messaging.yaml index 2355cfed85..70e195174f 100644 --- a/.github/workflows/functional-messaging.yaml +++ b/.github/workflows/functional-messaging.yaml @@ -39,7 +39,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.11 + uses: EmilienM/devstack-action@c41f86d8df58b53c55f070207b6dfce656788cfd with: branch: ${{ matrix.openstack_version }} conf_overrides: | diff --git a/.github/workflows/functional-networking.yaml b/.github/workflows/functional-networking.yaml index 8539acc80c..c3968a04e4 100644 --- a/.github/workflows/functional-networking.yaml +++ b/.github/workflows/functional-networking.yaml @@ -39,7 +39,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.11 + uses: EmilienM/devstack-action@c41f86d8df58b53c55f070207b6dfce656788cfd with: branch: ${{ matrix.openstack_version }} conf_overrides: | diff --git a/.github/workflows/functional-objectstorage.yaml b/.github/workflows/functional-objectstorage.yaml index 60cd27e9f2..30d07b877c 100644 --- a/.github/workflows/functional-objectstorage.yaml +++ b/.github/workflows/functional-objectstorage.yaml @@ -39,7 +39,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.11 + uses: EmilienM/devstack-action@c41f86d8df58b53c55f070207b6dfce656788cfd with: branch: ${{ matrix.openstack_version }} conf_overrides: | diff --git a/.github/workflows/functional-orchestration.yaml b/.github/workflows/functional-orchestration.yaml index 73d461aac6..85dd87eaa2 100644 --- a/.github/workflows/functional-orchestration.yaml +++ b/.github/workflows/functional-orchestration.yaml @@ -39,7 +39,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.11 + uses: EmilienM/devstack-action@c41f86d8df58b53c55f070207b6dfce656788cfd with: branch: ${{ matrix.openstack_version }} conf_overrides: | diff --git a/.github/workflows/functional-placement.yaml b/.github/workflows/functional-placement.yaml index 016157562d..c02b7d61a5 100644 --- a/.github/workflows/functional-placement.yaml +++ b/.github/workflows/functional-placement.yaml @@ -39,7 +39,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.11 + uses: EmilienM/devstack-action@c41f86d8df58b53c55f070207b6dfce656788cfd with: branch: ${{ matrix.openstack_version }} - name: Checkout go diff --git a/.github/workflows/functional-sharedfilesystems.yaml b/.github/workflows/functional-sharedfilesystems.yaml index 6924d001fe..0750dc4893 100644 --- a/.github/workflows/functional-sharedfilesystems.yaml +++ b/.github/workflows/functional-sharedfilesystems.yaml @@ -39,7 +39,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v3 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.11 + uses: EmilienM/devstack-action@c41f86d8df58b53c55f070207b6dfce656788cfd with: branch: ${{ matrix.openstack_version }} conf_overrides: | From ac1ecd42da6eec266c6c8b3cad9ce4d45ffcf62d Mon Sep 17 00:00:00 2001 From: Pierre Prinetti <pierreprinetti@redhat.com> Date: Tue, 20 Feb 2024 13:56:34 +0100 Subject: [PATCH 337/360] Fix AllowReauth reauthentication Due to an error in implementing the addition of context.Context, the default reauth function caught the context passed when generating the ProviderClient, which could be long canceled when the reauthentication takes place. --- openstack/client.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openstack/client.go b/openstack/client.go index 8c6173a264..acaa02be46 100644 --- a/openstack/client.go +++ b/openstack/client.go @@ -171,7 +171,7 @@ func v2auth(ctx context.Context, client *gophercloud.ProviderClient, endpoint st tao := options tao.AllowReauth = false client.ReauthFunc = func() error { - err := v2auth(ctx, &tac, endpoint, tao, eo) + err := v2auth(context.Background(), &tac, endpoint, tao, eo) if err != nil { return err } @@ -294,7 +294,7 @@ func v3auth(ctx context.Context, client *gophercloud.ProviderClient, endpoint st tao = opts } client.ReauthFunc = func() error { - err := v3auth(ctx, &tac, endpoint, tao, eo) + err := v3auth(context.Background(), &tac, endpoint, tao, eo) if err != nil { return err } From 3323f810f901410f775f6ea36f00311409075bd6 Mon Sep 17 00:00:00 2001 From: Stephen Finucane <stephenfin@redhat.com> Date: Wed, 21 Feb 2024 18:11:01 +0000 Subject: [PATCH 338/360] compute: Use volumeID, not attachmentID for volume attachments The volume attachments API operates on volumes, not attachments. Correct the variable name. Signed-off-by: Stephen Finucane <stephenfin@redhat.com> Closes: #2861 --- openstack/compute/v2/extensions/volumeattach/doc.go | 4 ++-- openstack/compute/v2/extensions/volumeattach/requests.go | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/openstack/compute/v2/extensions/volumeattach/doc.go b/openstack/compute/v2/extensions/volumeattach/doc.go index 484eb20000..857ab19cc5 100644 --- a/openstack/compute/v2/extensions/volumeattach/doc.go +++ b/openstack/compute/v2/extensions/volumeattach/doc.go @@ -20,9 +20,9 @@ Example to Attach a Volume Example to Detach a Volume serverID := "7ac8686c-de71-4acb-9600-ec18b1a1ed6d" - attachmentID := "ed081613-1c9b-4231-aa5e-ebfd4d87f983" + volumeID := "ed081613-1c9b-4231-aa5e-ebfd4d87f983" - err := volumeattach.Delete(computeClient, serverID, attachmentID).ExtractErr() + err := volumeattach.Delete(computeClient, serverID, volumeID).ExtractErr() if err != nil { panic(err) } diff --git a/openstack/compute/v2/extensions/volumeattach/requests.go b/openstack/compute/v2/extensions/volumeattach/requests.go index 8c5a2ba03e..fe0b1075d6 100644 --- a/openstack/compute/v2/extensions/volumeattach/requests.go +++ b/openstack/compute/v2/extensions/volumeattach/requests.go @@ -56,16 +56,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, attachmentID string) (r GetResult) { - resp, err := client.Get(getURL(client, serverID, attachmentID), &r.Body, nil) +func Get(client *gophercloud.ServiceClient, serverID, volumeID string) (r GetResult) { + resp, err := client.Get(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, attachmentID string) (r DeleteResult) { - resp, err := client.Delete(deleteURL(client, serverID, attachmentID), nil) +func Delete(client *gophercloud.ServiceClient, serverID, volumeID string) (r DeleteResult) { + resp, err := client.Delete(deleteURL(client, serverID, volumeID), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } From e31725833415a925c66f69bfd84fa9d53bbd142f Mon Sep 17 00:00:00 2001 From: Pierre Prinetti <pierreprinetti@redhat.com> Date: Tue, 27 Feb 2024 16:12:49 +0100 Subject: [PATCH 339/360] Prepare v1.10.0 --- CHANGELOG.md | 10 ++++++++++ provider_client.go | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e10078ade6..3d99d01ff2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,13 @@ +## v1.10.0 (2024-02-27) + +* [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) New features and improvements: diff --git a/provider_client.go b/provider_client.go index 5cb5e150ff..64787e5337 100644 --- a/provider_client.go +++ b/provider_client.go @@ -16,7 +16,7 @@ import ( // DefaultUserAgent is the default User-Agent string set in the request header. const ( - DefaultUserAgent = "gophercloud/v1.9.0" + DefaultUserAgent = "gophercloud/v1.10.0" DefaultMaxBackoffRetries = 60 ) From 16b3cd52f754a6fcdb11e3a091b1fa467ce23779 Mon Sep 17 00:00:00 2001 From: Pierre Prinetti <pierreprinetti@redhat.com> Date: Wed, 6 Mar 2024 16:51:40 +0100 Subject: [PATCH 340/360] Revert "Fix AllowReauth reauthentication" This reverts commit ac1ecd42da6eec266c6c8b3cad9ce4d45ffcf62d. --- openstack/client.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openstack/client.go b/openstack/client.go index acaa02be46..8c6173a264 100644 --- a/openstack/client.go +++ b/openstack/client.go @@ -171,7 +171,7 @@ func v2auth(ctx context.Context, client *gophercloud.ProviderClient, endpoint st tao := options tao.AllowReauth = false client.ReauthFunc = func() error { - err := v2auth(context.Background(), &tac, endpoint, tao, eo) + err := v2auth(ctx, &tac, endpoint, tao, eo) if err != nil { return err } @@ -294,7 +294,7 @@ func v3auth(ctx context.Context, client *gophercloud.ProviderClient, endpoint st tao = opts } client.ReauthFunc = func() error { - err := v3auth(context.Background(), &tac, endpoint, tao, eo) + err := v3auth(ctx, &tac, endpoint, tao, eo) if err != nil { return err } From fbfac92f640786a02ed622021124c13dcd15d292 Mon Sep 17 00:00:00 2001 From: Pierre Prinetti <pierreprinetti@redhat.com> Date: Wed, 6 Mar 2024 16:52:14 +0100 Subject: [PATCH 341/360] Revert "clouds: export ParseOption to allow conditionally compose With* opts" This reverts commit 018a0795e6715ceea69113e8d484c6fb42bcea1f. --- openstack/config/clouds/clouds.go | 2 +- openstack/config/clouds/options.go | 49 ++++++++++++++---------------- 2 files changed, 24 insertions(+), 27 deletions(-) diff --git a/openstack/config/clouds/clouds.go b/openstack/config/clouds/clouds.go index 54f3e8774b..d16801adfa 100644 --- a/openstack/config/clouds/clouds.go +++ b/openstack/config/clouds/clouds.go @@ -53,7 +53,7 @@ import ( // // Search locations, as well as individual `clouds.yaml` properties, can be // overwritten with functional options. -func Parse(opts ...ParseOption) (gophercloud.AuthOptions, gophercloud.EndpointOpts, *tls.Config, error) { +func Parse(opts ...func(*cloudOpts)) (gophercloud.AuthOptions, gophercloud.EndpointOpts, *tls.Config, error) { options := cloudOpts{ cloudName: os.Getenv("OS_CLOUD"), region: os.Getenv("OS_REGION_NAME"), diff --git a/openstack/config/clouds/options.go b/openstack/config/clouds/options.go index 459c217555..30bf6a2ef3 100644 --- a/openstack/config/clouds/options.go +++ b/openstack/config/clouds/options.go @@ -34,11 +34,8 @@ type cloudOpts struct { insecure *bool } -// ParseOption one of parse configuration returned by With* modifier -type ParseOption = func(*cloudOpts) - // WithCloudName allows to override the environment variable `OS_CLOUD`. -func WithCloudName(osCloud string) ParseOption { +func WithCloudName(osCloud string) func(*cloudOpts) { return func(co *cloudOpts) { co.cloudName = osCloud } @@ -47,7 +44,7 @@ func WithCloudName(osCloud string) ParseOption { // WithLocations is a functional option that sets the search locations for the // clouds.yaml file (and its optional companion secure.yaml). Each location is // a file path pointing to a possible `clouds.yaml`. -func WithLocations(locations ...string) ParseOption { +func WithLocations(locations ...string) func(*cloudOpts) { return func(co *cloudOpts) { co.locations = locations } @@ -57,7 +54,7 @@ func WithLocations(locations ...string) ParseOption { // as an io.Reader interface. When this option is passed, FromCloudsYaml will // not attempt to fetch any file from the file system. To add a secure.yaml, // use in conjunction with WithSecureYAML. -func WithCloudsYAML(clouds io.Reader) ParseOption { +func WithCloudsYAML(clouds io.Reader) func(*cloudOpts) { return func(co *cloudOpts) { co.cloudsyamlReader = clouds } @@ -66,43 +63,43 @@ func WithCloudsYAML(clouds io.Reader) ParseOption { // WithSecureYAML is a functional option that lets you pass a secure.yaml file // as an io.Reader interface, to complement the clouds.yaml that is either // fetched from the filesystem, or passed with WithCloudsYAML. -func WithSecureYAML(secure io.Reader) ParseOption { +func WithSecureYAML(secure io.Reader) func(*cloudOpts) { return func(co *cloudOpts) { co.secureyamlReader = secure } } -func WithApplicationCredentialID(applicationCredentialID string) ParseOption { +func WithApplicationCredentialID(applicationCredentialID string) func(*cloudOpts) { return func(co *cloudOpts) { co.applicationCredentialID = applicationCredentialID } } -func WithApplicationCredentialName(applicationCredentialName string) ParseOption { +func WithApplicationCredentialName(applicationCredentialName string) func(*cloudOpts) { return func(co *cloudOpts) { co.applicationCredentialName = applicationCredentialName } } -func WithApplicationCredentialSecret(applicationCredentialSecret string) ParseOption { +func WithApplicationCredentialSecret(applicationCredentialSecret string) func(*cloudOpts) { return func(co *cloudOpts) { co.applicationCredentialSecret = applicationCredentialSecret } } -func WithIdentityEndpoint(authURL string) ParseOption { +func WithIdentityEndpoint(authURL string) func(*cloudOpts) { return func(co *cloudOpts) { co.authURL = authURL } } -func WithDomainID(domainID string) ParseOption { +func WithDomainID(domainID string) func(*cloudOpts) { return func(co *cloudOpts) { co.domainID = domainID } } -func WithDomainName(domainName string) ParseOption { +func WithDomainName(domainName string) func(*cloudOpts) { return func(co *cloudOpts) { co.domainName = domainName } @@ -110,25 +107,25 @@ func WithDomainName(domainName string) ParseOption { // WithRegion allows to override the endpoint type set in clouds.yaml or in the // environment variable `OS_INTERFACE`. -func WithEndpointType(endpointType string) ParseOption { +func WithEndpointType(endpointType string) func(*cloudOpts) { return func(co *cloudOpts) { co.endpointType = endpointType } } -func WithPassword(password string) ParseOption { +func WithPassword(password string) func(*cloudOpts) { return func(co *cloudOpts) { co.password = password } } -func WithProjectID(projectID string) ParseOption { +func WithProjectID(projectID string) func(*cloudOpts) { return func(co *cloudOpts) { co.projectID = projectID } } -func WithProjectName(projectName string) ParseOption { +func WithProjectName(projectName string) func(*cloudOpts) { return func(co *cloudOpts) { co.projectName = projectName } @@ -136,55 +133,55 @@ func WithProjectName(projectName string) ParseOption { // WithRegion allows to override the region set in clouds.yaml or in the // environment variable `OS_REGION_NAME` -func WithRegion(region string) ParseOption { +func WithRegion(region string) func(*cloudOpts) { return func(co *cloudOpts) { co.region = region } } -func WithScope(scope *gophercloud.AuthScope) ParseOption { +func WithScope(scope *gophercloud.AuthScope) func(*cloudOpts) { return func(co *cloudOpts) { co.scope = scope } } -func WithToken(token string) ParseOption { +func WithToken(token string) func(*cloudOpts) { return func(co *cloudOpts) { co.token = token } } -func WithUserID(userID string) ParseOption { +func WithUserID(userID string) func(*cloudOpts) { return func(co *cloudOpts) { co.userID = userID } } -func WithUsername(username string) ParseOption { +func WithUsername(username string) func(*cloudOpts) { return func(co *cloudOpts) { co.username = username } } -func WithCACertPath(caCertPath string) ParseOption { +func WithCACertPath(caCertPath string) func(*cloudOpts) { return func(co *cloudOpts) { co.caCertPath = caCertPath } } -func WithClientCertPath(clientCertPath string) ParseOption { +func WithClientCertPath(clientCertPath string) func(*cloudOpts) { return func(co *cloudOpts) { co.clientCertPath = clientCertPath } } -func WithClientKeyPath(clientKeyPath string) ParseOption { +func WithClientKeyPath(clientKeyPath string) func(*cloudOpts) { return func(co *cloudOpts) { co.clientKeyPath = clientKeyPath } } -func WithInsecure(insecure bool) ParseOption { +func WithInsecure(insecure bool) func(*cloudOpts) { return func(co *cloudOpts) { co.insecure = &insecure } From 52d12eccaffe113fb25b4f51c0ac78e17073dfab Mon Sep 17 00:00:00 2001 From: Pierre Prinetti <pierreprinetti@redhat.com> Date: Wed, 6 Mar 2024 16:52:26 +0100 Subject: [PATCH 342/360] Revert "clouds: fix default /etc/openstack/clouds.yaml selection" This reverts commit af248d5a5dc495630277e7760a7d58eadb2301b4. --- openstack/config/clouds/clouds.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openstack/config/clouds/clouds.go b/openstack/config/clouds/clouds.go index d16801adfa..b04416e68d 100644 --- a/openstack/config/clouds/clouds.go +++ b/openstack/config/clouds/clouds.go @@ -86,7 +86,7 @@ func Parse(opts ...func(*cloudOpts)) (gophercloud.AuthOptions, gophercloud.Endpo if err != nil { return gophercloud.AuthOptions{}, gophercloud.EndpointOpts{}, nil, fmt.Errorf("failed to get the user config directory: %w", err) } - options.locations = []string{path.Join(cwd, "clouds.yaml"), path.Join(userConfig, "openstack", "clouds.yaml"), path.Join("/etc", "openstack", "clouds.yaml")} + options.locations = []string{path.Join(cwd, "clouds.yaml"), path.Join(userConfig, "openstack", "clouds.yaml"), path.Join("/etc", "openstack")} } for _, cloudsPath := range options.locations { From da1523b7ae462acf6d0ef89af94acd3d54150aa0 Mon Sep 17 00:00:00 2001 From: Pierre Prinetti <pierreprinetti@redhat.com> Date: Wed, 6 Mar 2024 16:52:38 +0100 Subject: [PATCH 343/360] Revert "Authenticate with a clouds.yaml" This reverts commit 22c56829f05af19f3ab422ff1b8b9db2000789f9. --- openstack/config/clouds/clouds.go | 271 ------------------------- openstack/config/clouds/clouds_test.go | 64 ------ openstack/config/clouds/options.go | 188 ----------------- openstack/config/clouds/tls.go | 88 -------- openstack/config/clouds/types.go | 203 ------------------ openstack/config/provider_client.go | 70 ------- 6 files changed, 884 deletions(-) delete mode 100644 openstack/config/clouds/clouds.go delete mode 100644 openstack/config/clouds/clouds_test.go delete mode 100644 openstack/config/clouds/options.go delete mode 100644 openstack/config/clouds/tls.go delete mode 100644 openstack/config/clouds/types.go delete mode 100644 openstack/config/provider_client.go diff --git a/openstack/config/clouds/clouds.go b/openstack/config/clouds/clouds.go deleted file mode 100644 index b04416e68d..0000000000 --- a/openstack/config/clouds/clouds.go +++ /dev/null @@ -1,271 +0,0 @@ -// package clouds provides a parser for OpenStack credentials stored in a clouds.yaml file. -// -// Example use: -// -// ctx := context.Background() -// ao, eo, tlsConfig, err := clouds.Parse() -// if err != nil { -// panic(err) -// } -// -// providerClient, err := config.NewProviderClient(ctx, ao, config.WithTLSConfig(tlsConfig)) -// if err != nil { -// panic(err) -// } -// -// networkClient, err := openstack.NewNetworkV2(providerClient, eo) -// if err != nil { -// panic(err) -// } -package clouds - -import ( - "crypto/tls" - "encoding/json" - "errors" - "fmt" - "os" - "path" - "reflect" - - "github.com/gophercloud/gophercloud" - "gopkg.in/yaml.v2" -) - -// Parse fetches a clouds.yaml file from disk and returns the parsed -// credentials. -// -// By default this function mimics the behaviour of python-openstackclient, which is: -// -// - if the environment variable `OS_CLIENT_CONFIG_FILE` is set and points to a -// clouds.yaml, use that location as the only search location for `clouds.yaml` and `secure.yaml`; -// - otherwise, the search locations for `clouds.yaml` and `secure.yaml` are: -// 1. the current working directory (on Linux: `./`) -// 2. the directory `openstack` under the standatd user config location for -// the operating system (on Linux: `${XDG_CONFIG_HOME:-$HOME/.config}/openstack/`) -// 3. on Linux, `/etc/openstack/` -// -// Once `clouds.yaml` is found in a search location, the same location is used to search for `secure.yaml`. -// -// Like in python-openstackclient, relative paths in the `clouds.yaml` section -// `cacert` are interpreted as relative the the current directory, and not to -// the `clouds.yaml` location. -// -// Search locations, as well as individual `clouds.yaml` properties, can be -// overwritten with functional options. -func Parse(opts ...func(*cloudOpts)) (gophercloud.AuthOptions, gophercloud.EndpointOpts, *tls.Config, error) { - options := cloudOpts{ - cloudName: os.Getenv("OS_CLOUD"), - region: os.Getenv("OS_REGION_NAME"), - endpointType: os.Getenv("OS_INTERFACE"), - locations: func() []string { - if path := os.Getenv("OS_CLIENT_CONFIG_FILE"); path != "" { - return []string{path} - } - return nil - }(), - } - - for _, apply := range opts { - apply(&options) - } - - if options.cloudName == "" { - return gophercloud.AuthOptions{}, gophercloud.EndpointOpts{}, nil, fmt.Errorf("the empty string \"\" is not a valid cloud name") - } - - // Set the defaults and open the files for reading. This code only runs - // if no override has been set, because it is fallible. - if options.cloudsyamlReader == nil { - if len(options.locations) < 1 { - cwd, err := os.Getwd() - if err != nil { - return gophercloud.AuthOptions{}, gophercloud.EndpointOpts{}, nil, fmt.Errorf("failed to get the current working directory: %w", err) - } - userConfig, err := os.UserConfigDir() - if err != nil { - return gophercloud.AuthOptions{}, gophercloud.EndpointOpts{}, nil, fmt.Errorf("failed to get the user config directory: %w", err) - } - options.locations = []string{path.Join(cwd, "clouds.yaml"), path.Join(userConfig, "openstack", "clouds.yaml"), path.Join("/etc", "openstack")} - } - - for _, cloudsPath := range options.locations { - var errNotFound *os.PathError - f, err := os.Open(cloudsPath) - if err != nil && !errors.As(err, &errNotFound) { - return gophercloud.AuthOptions{}, gophercloud.EndpointOpts{}, nil, fmt.Errorf("failed to open %q: %w", cloudsPath, err) - } - if err == nil { - defer f.Close() - options.cloudsyamlReader = f - - if options.secureyamlReader == nil { - securePath := path.Join(path.Base(cloudsPath), "secure.yaml") - secureF, err := os.Open(securePath) - if err != nil && !errors.As(err, &errNotFound) { - return gophercloud.AuthOptions{}, gophercloud.EndpointOpts{}, nil, fmt.Errorf("failed to open %q: %w", securePath, err) - } - if err == nil { - defer secureF.Close() - options.secureyamlReader = secureF - } - } - } - } - if options.cloudsyamlReader == nil { - return gophercloud.AuthOptions{}, gophercloud.EndpointOpts{}, nil, fmt.Errorf("clouds file not found. Search locations were: %v", options.locations) - } - } - - // Parse the YAML payloads. - var clouds Clouds - if err := yaml.NewDecoder(options.cloudsyamlReader).Decode(&clouds); err != nil { - return gophercloud.AuthOptions{}, gophercloud.EndpointOpts{}, nil, err - } - - cloud, ok := clouds.Clouds[options.cloudName] - if !ok { - return gophercloud.AuthOptions{}, gophercloud.EndpointOpts{}, nil, fmt.Errorf("cloud %q not found in clouds.yaml", options.cloudName) - } - - if options.secureyamlReader != nil { - var secureClouds Clouds - if err := yaml.NewDecoder(options.secureyamlReader).Decode(&secureClouds); err != nil { - return gophercloud.AuthOptions{}, gophercloud.EndpointOpts{}, nil, fmt.Errorf("failed to parse secure.yaml: %w", err) - } - - if secureCloud, ok := secureClouds.Clouds[options.cloudName]; ok { - // If secureCloud has content and it differs from the cloud entry, - // merge the two together. - if !reflect.DeepEqual((gophercloud.AuthOptions{}), secureClouds) && !reflect.DeepEqual(clouds, secureClouds) { - var err error - cloud, err = mergeClouds(secureCloud, cloud) - if err != nil { - return gophercloud.AuthOptions{}, gophercloud.EndpointOpts{}, nil, fmt.Errorf("unable to merge information from clouds.yaml and secure.yaml") - } - } - } - } - - tlsConfig, err := computeTLSConfig(cloud, options) - if err != nil { - return gophercloud.AuthOptions{}, gophercloud.EndpointOpts{}, nil, fmt.Errorf("unable to compute TLS configuration: %w", err) - } - - endpointType := coalesce(options.endpointType, cloud.EndpointType, cloud.Interface) - - return gophercloud.AuthOptions{ - IdentityEndpoint: coalesce(options.authURL, cloud.AuthInfo.AuthURL), - Username: coalesce(options.username, cloud.AuthInfo.Username), - UserID: coalesce(options.userID, cloud.AuthInfo.UserID), - Password: coalesce(options.password, cloud.AuthInfo.Password), - DomainID: coalesce(options.domainID, cloud.AuthInfo.UserDomainID, cloud.AuthInfo.ProjectDomainID, cloud.AuthInfo.DomainID), - DomainName: coalesce(options.domainName, cloud.AuthInfo.UserDomainName, cloud.AuthInfo.ProjectDomainName, cloud.AuthInfo.DomainName), - TenantID: coalesce(options.projectID, cloud.AuthInfo.ProjectID), - TenantName: coalesce(options.projectName, cloud.AuthInfo.ProjectName), - TokenID: coalesce(options.token, cloud.AuthInfo.Token), - Scope: options.scope, - ApplicationCredentialID: coalesce(options.applicationCredentialID, cloud.AuthInfo.ApplicationCredentialID), - ApplicationCredentialName: coalesce(options.applicationCredentialName, cloud.AuthInfo.ApplicationCredentialName), - ApplicationCredentialSecret: coalesce(options.applicationCredentialSecret, cloud.AuthInfo.ApplicationCredentialSecret), - }, gophercloud.EndpointOpts{ - Region: coalesce(options.region, cloud.RegionName), - Availability: computeAvailability(endpointType), - }, - tlsConfig, - nil -} - -// computeAvailability is a helper method to determine the endpoint type -// requested by the user. -func computeAvailability(endpointType string) gophercloud.Availability { - if endpointType == "internal" || endpointType == "internalURL" { - return gophercloud.AvailabilityInternal - } - if endpointType == "admin" || endpointType == "adminURL" { - return gophercloud.AvailabilityAdmin - } - return gophercloud.AvailabilityPublic -} - -// coalesce returns the first argument that is not the empty string, or the -// empty string. -func coalesce(items ...string) string { - for _, item := range items { - if item != "" { - return item - } - } - return "" -} - -// mergeClouds merges two Clouds recursively (the AuthInfo also gets merged). -// In case both Clouds define a value, the value in the 'override' cloud takes precedence -func mergeClouds(override, cloud Cloud) (Cloud, error) { - overrideJson, err := json.Marshal(override) - if err != nil { - return Cloud{}, err - } - cloudJson, err := json.Marshal(cloud) - if err != nil { - return Cloud{}, err - } - var overrideInterface interface{} - err = json.Unmarshal(overrideJson, &overrideInterface) - if err != nil { - return Cloud{}, err - } - var cloudInterface interface{} - err = json.Unmarshal(cloudJson, &cloudInterface) - if err != nil { - return Cloud{}, err - } - var mergedCloud Cloud - mergedInterface := mergeInterfaces(overrideInterface, cloudInterface) - mergedJson, err := json.Marshal(mergedInterface) - err = json.Unmarshal(mergedJson, &mergedCloud) - if err != nil { - return Cloud{}, err - } - return mergedCloud, nil -} - -// merges two interfaces. In cases where a value is defined for both 'overridingInterface' and -// 'inferiorInterface' the value in 'overridingInterface' will take precedence. -func mergeInterfaces(overridingInterface, inferiorInterface interface{}) interface{} { - switch overriding := overridingInterface.(type) { - case map[string]interface{}: - interfaceMap, ok := inferiorInterface.(map[string]interface{}) - if !ok { - return overriding - } - for k, v := range interfaceMap { - if overridingValue, ok := overriding[k]; ok { - overriding[k] = mergeInterfaces(overridingValue, v) - } else { - overriding[k] = v - } - } - case []interface{}: - list, ok := inferiorInterface.([]interface{}) - if !ok { - return overriding - } - for i := range list { - overriding = append(overriding, list[i]) - } - return overriding - case nil: - // mergeClouds(nil, map[string]interface{...}) -> map[string]interface{...} - v, ok := inferiorInterface.(map[string]interface{}) - if ok { - return v - } - } - // We don't want to override with empty values - if reflect.DeepEqual(overridingInterface, nil) || reflect.DeepEqual(reflect.Zero(reflect.TypeOf(overridingInterface)).Interface(), overridingInterface) { - return inferiorInterface - } else { - return overridingInterface - } -} diff --git a/openstack/config/clouds/clouds_test.go b/openstack/config/clouds/clouds_test.go deleted file mode 100644 index e25bae0122..0000000000 --- a/openstack/config/clouds/clouds_test.go +++ /dev/null @@ -1,64 +0,0 @@ -package clouds_test - -import ( - "fmt" - "strings" - - "github.com/gophercloud/gophercloud/openstack/config/clouds" -) - -func ExampleWithCloudName() { - const exampleClouds = `clouds: - openstack: - auth: - auth_url: https://example.com:13000` - - ao, _, _, err := clouds.Parse( - clouds.WithCloudsYAML(strings.NewReader(exampleClouds)), - clouds.WithCloudName("openstack"), - ) - if err != nil { - panic(err) - } - - fmt.Println(ao.IdentityEndpoint) - // Output: https://example.com:13000 -} - -func ExampleWithUserID() { - const exampleClouds = `clouds: - openstack: - auth: - auth_url: https://example.com:13000` - - ao, _, _, err := clouds.Parse( - clouds.WithCloudsYAML(strings.NewReader(exampleClouds)), - clouds.WithCloudName("openstack"), - clouds.WithUsername("Kris"), - ) - if err != nil { - panic(err) - } - - fmt.Println(ao.Username) - // Output: Kris -} - -func ExampleWithRegion() { - const exampleClouds = `clouds: - openstack: - auth: - auth_url: https://example.com:13000` - - _, eo, _, err := clouds.Parse( - clouds.WithCloudsYAML(strings.NewReader(exampleClouds)), - clouds.WithCloudName("openstack"), - clouds.WithRegion("mars"), - ) - if err != nil { - panic(err) - } - - fmt.Println(eo.Region) - // Output: mars -} diff --git a/openstack/config/clouds/options.go b/openstack/config/clouds/options.go deleted file mode 100644 index 30bf6a2ef3..0000000000 --- a/openstack/config/clouds/options.go +++ /dev/null @@ -1,188 +0,0 @@ -package clouds - -import ( - "io" - - "github.com/gophercloud/gophercloud" -) - -type cloudOpts struct { - cloudName string - locations []string - cloudsyamlReader io.Reader - secureyamlReader io.Reader - - applicationCredentialID string - applicationCredentialName string - applicationCredentialSecret string - authURL string - domainID string - domainName string - endpointType string - password string - projectID string - projectName string - region string - scope *gophercloud.AuthScope - token string - userID string - username string - - caCertPath string - clientCertPath string - clientKeyPath string - insecure *bool -} - -// WithCloudName allows to override the environment variable `OS_CLOUD`. -func WithCloudName(osCloud string) func(*cloudOpts) { - return func(co *cloudOpts) { - co.cloudName = osCloud - } -} - -// WithLocations is a functional option that sets the search locations for the -// clouds.yaml file (and its optional companion secure.yaml). Each location is -// a file path pointing to a possible `clouds.yaml`. -func WithLocations(locations ...string) func(*cloudOpts) { - return func(co *cloudOpts) { - co.locations = locations - } -} - -// WithCloudsYAML is a functional option that lets you pass a clouds.yaml file -// as an io.Reader interface. When this option is passed, FromCloudsYaml will -// not attempt to fetch any file from the file system. To add a secure.yaml, -// use in conjunction with WithSecureYAML. -func WithCloudsYAML(clouds io.Reader) func(*cloudOpts) { - return func(co *cloudOpts) { - co.cloudsyamlReader = clouds - } -} - -// WithSecureYAML is a functional option that lets you pass a secure.yaml file -// as an io.Reader interface, to complement the clouds.yaml that is either -// fetched from the filesystem, or passed with WithCloudsYAML. -func WithSecureYAML(secure io.Reader) func(*cloudOpts) { - return func(co *cloudOpts) { - co.secureyamlReader = secure - } -} - -func WithApplicationCredentialID(applicationCredentialID string) func(*cloudOpts) { - return func(co *cloudOpts) { - co.applicationCredentialID = applicationCredentialID - } -} - -func WithApplicationCredentialName(applicationCredentialName string) func(*cloudOpts) { - return func(co *cloudOpts) { - co.applicationCredentialName = applicationCredentialName - } -} - -func WithApplicationCredentialSecret(applicationCredentialSecret string) func(*cloudOpts) { - return func(co *cloudOpts) { - co.applicationCredentialSecret = applicationCredentialSecret - } -} - -func WithIdentityEndpoint(authURL string) func(*cloudOpts) { - return func(co *cloudOpts) { - co.authURL = authURL - } -} - -func WithDomainID(domainID string) func(*cloudOpts) { - return func(co *cloudOpts) { - co.domainID = domainID - } -} - -func WithDomainName(domainName string) func(*cloudOpts) { - return func(co *cloudOpts) { - co.domainName = domainName - } -} - -// WithRegion allows to override the endpoint type set in clouds.yaml or in the -// environment variable `OS_INTERFACE`. -func WithEndpointType(endpointType string) func(*cloudOpts) { - return func(co *cloudOpts) { - co.endpointType = endpointType - } -} - -func WithPassword(password string) func(*cloudOpts) { - return func(co *cloudOpts) { - co.password = password - } -} - -func WithProjectID(projectID string) func(*cloudOpts) { - return func(co *cloudOpts) { - co.projectID = projectID - } -} - -func WithProjectName(projectName string) func(*cloudOpts) { - return func(co *cloudOpts) { - co.projectName = projectName - } -} - -// WithRegion allows to override the region set in clouds.yaml or in the -// environment variable `OS_REGION_NAME` -func WithRegion(region string) func(*cloudOpts) { - return func(co *cloudOpts) { - co.region = region - } -} - -func WithScope(scope *gophercloud.AuthScope) func(*cloudOpts) { - return func(co *cloudOpts) { - co.scope = scope - } -} - -func WithToken(token string) func(*cloudOpts) { - return func(co *cloudOpts) { - co.token = token - } -} - -func WithUserID(userID string) func(*cloudOpts) { - return func(co *cloudOpts) { - co.userID = userID - } -} - -func WithUsername(username string) func(*cloudOpts) { - return func(co *cloudOpts) { - co.username = username - } -} - -func WithCACertPath(caCertPath string) func(*cloudOpts) { - return func(co *cloudOpts) { - co.caCertPath = caCertPath - } -} - -func WithClientCertPath(clientCertPath string) func(*cloudOpts) { - return func(co *cloudOpts) { - co.clientCertPath = clientCertPath - } -} - -func WithClientKeyPath(clientKeyPath string) func(*cloudOpts) { - return func(co *cloudOpts) { - co.clientKeyPath = clientKeyPath - } -} - -func WithInsecure(insecure bool) func(*cloudOpts) { - return func(co *cloudOpts) { - co.insecure = &insecure - } -} diff --git a/openstack/config/clouds/tls.go b/openstack/config/clouds/tls.go deleted file mode 100644 index 460ede6d04..0000000000 --- a/openstack/config/clouds/tls.go +++ /dev/null @@ -1,88 +0,0 @@ -package clouds - -import ( - "bytes" - "crypto/tls" - "crypto/x509" - "fmt" - "io/ioutil" - "os" - "path" - "strings" -) - -func computeTLSConfig(cloud Cloud, options cloudOpts) (*tls.Config, error) { - tlsConfig := new(tls.Config) - if caCertPath := coalesce(options.caCertPath, os.Getenv("OS_CACERT"), cloud.CACertFile); caCertPath != "" { - caCertPath, err := resolveTilde(caCertPath) - if err != nil { - return nil, fmt.Errorf("failed to resolve user home directory: %w", err) - } - - caCert, err := ioutil.ReadFile(caCertPath) - if err != nil { - return nil, fmt.Errorf("failed to open the CA cert file: %w", err) - } - - caCertPool := x509.NewCertPool() - if ok := caCertPool.AppendCertsFromPEM(bytes.TrimSpace(caCert)); !ok { - return nil, fmt.Errorf("failed to parse the CA Cert from %q", caCertPath) - } - tlsConfig.RootCAs = caCertPool - } - - tlsConfig.InsecureSkipVerify = func() bool { - if options.insecure != nil { - return *options.insecure - } - if cloud.Verify != nil { - return !*cloud.Verify - } - return false - }() - - if clientCertPath, clientKeyPath := coalesce(options.clientCertPath, os.Getenv("OS_CERT"), cloud.ClientCertFile), coalesce(options.clientKeyPath, os.Getenv("OS_KEY"), cloud.ClientKeyFile); clientCertPath != "" && clientKeyPath != "" { - clientCertPath, err := resolveTilde(clientCertPath) - if err != nil { - return nil, fmt.Errorf("failed to resolve user home directory in client cert path: %w", err) - } - clientKeyPath, err := resolveTilde(clientKeyPath) - if err != nil { - return nil, fmt.Errorf("failed to resolve user home directory in client cert key path: %w", err) - } - - clientCert, err := ioutil.ReadFile(clientCertPath) - if err != nil { - return nil, fmt.Errorf("failed to read the client cert file: %w", err) - } - - clientKey, err := ioutil.ReadFile(clientKeyPath) - if err != nil { - return nil, fmt.Errorf("failed to read the client cert key file: %w", err) - } - - cert, err := tls.X509KeyPair(clientCert, clientKey) - if err != nil { - return nil, err - } - - tlsConfig.Certificates = []tls.Certificate{cert} - } else if clientCertPath != "" && clientKeyPath == "" { - return nil, fmt.Errorf("client cert is set, but client cert key is missing") - } else if clientCertPath == "" && clientKeyPath != "" { - return nil, fmt.Errorf("client cert key is set, but client cert is missing") - } - - return tlsConfig, nil -} - -func resolveTilde(p string) (string, error) { - if after := strings.TrimPrefix(p, "~/"); after != p { - h, err := os.UserHomeDir() - if err != nil { - return "", fmt.Errorf("failed to resolve user home directory: %w", err) - } - return path.Join(h, after), nil - } - return p, nil -} diff --git a/openstack/config/clouds/types.go b/openstack/config/clouds/types.go deleted file mode 100644 index c1914633ff..0000000000 --- a/openstack/config/clouds/types.go +++ /dev/null @@ -1,203 +0,0 @@ -package clouds - -import "encoding/json" - -// Clouds represents a collection of Cloud entries in a clouds.yaml file. -// The format of clouds.yaml is documented at -// https://docs.openstack.org/os-client-config/latest/user/configuration.html. -type Clouds struct { - Clouds map[string]Cloud `yaml:"clouds" json:"clouds"` -} - -// Cloud represents an entry in a clouds.yaml/public-clouds.yaml/secure.yaml file. -type Cloud struct { - Cloud string `yaml:"cloud,omitempty" json:"cloud,omitempty"` - Profile string `yaml:"profile,omitempty" json:"profile,omitempty"` - AuthInfo *AuthInfo `yaml:"auth,omitempty" json:"auth,omitempty"` - AuthType AuthType `yaml:"auth_type,omitempty" json:"auth_type,omitempty"` - RegionName string `yaml:"region_name,omitempty" json:"region_name,omitempty"` - Regions []Region `yaml:"regions,omitempty" json:"regions,omitempty"` - - // EndpointType and Interface both specify whether to use the public, internal, - // or admin interface of a service. They should be considered synonymous, but - // EndpointType will take precedence when both are specified. - EndpointType string `yaml:"endpoint_type,omitempty" json:"endpoint_type,omitempty"` - Interface string `yaml:"interface,omitempty" json:"interface,omitempty"` - - // API Version overrides. - IdentityAPIVersion string `yaml:"identity_api_version,omitempty" json:"identity_api_version,omitempty"` - VolumeAPIVersion string `yaml:"volume_api_version,omitempty" json:"volume_api_version,omitempty"` - - // Verify whether or not SSL API requests should be verified. - Verify *bool `yaml:"verify,omitempty" json:"verify,omitempty"` - - // CACertFile a path to a CA Cert bundle that can be used as part of - // verifying SSL API requests. - CACertFile string `yaml:"cacert,omitempty" json:"cacert,omitempty"` - - // ClientCertFile a path to a client certificate to use as part of the SSL - // transaction. - ClientCertFile string `yaml:"cert,omitempty" json:"cert,omitempty"` - - // ClientKeyFile a path to a client key to use as part of the SSL - // transaction. - ClientKeyFile string `yaml:"key,omitempty" json:"key,omitempty"` -} - -// AuthInfo represents the auth section of a cloud entry or -// auth options entered explicitly in ClientOpts. -type AuthInfo struct { - // AuthURL is the keystone/identity endpoint URL. - AuthURL string `yaml:"auth_url,omitempty" json:"auth_url,omitempty"` - - // Token is a pre-generated authentication token. - Token string `yaml:"token,omitempty" json:"token,omitempty"` - - // Username is the username of the user. - Username string `yaml:"username,omitempty" json:"username,omitempty"` - - // UserID is the unique ID of a user. - UserID string `yaml:"user_id,omitempty" json:"user_id,omitempty"` - - // Password is the password of the user. - Password string `yaml:"password,omitempty" json:"password,omitempty"` - - // Application Credential ID to login with. - ApplicationCredentialID string `yaml:"application_credential_id,omitempty" json:"application_credential_id,omitempty"` - - // Application Credential name to login with. - ApplicationCredentialName string `yaml:"application_credential_name,omitempty" json:"application_credential_name,omitempty"` - - // Application Credential secret to login with. - ApplicationCredentialSecret string `yaml:"application_credential_secret,omitempty" json:"application_credential_secret,omitempty"` - - // SystemScope is a system information to scope to. - SystemScope string `yaml:"system_scope,omitempty" json:"system_scope,omitempty"` - - // ProjectName is the common/human-readable name of a project. - // Users can be scoped to a project. - // ProjectName on its own is not enough to ensure a unique scope. It must - // also be combined with either a ProjectDomainName or ProjectDomainID. - // ProjectName cannot be combined with ProjectID in a scope. - ProjectName string `yaml:"project_name,omitempty" json:"project_name,omitempty"` - - // ProjectID is the unique ID of a project. - // It can be used to scope a user to a specific project. - ProjectID string `yaml:"project_id,omitempty" json:"project_id,omitempty"` - - // UserDomainName is the name of the domain where a user resides. - // It is used to identify the source domain of a user. - UserDomainName string `yaml:"user_domain_name,omitempty" json:"user_domain_name,omitempty"` - - // UserDomainID is the unique ID of the domain where a user resides. - // It is used to identify the source domain of a user. - UserDomainID string `yaml:"user_domain_id,omitempty" json:"user_domain_id,omitempty"` - - // ProjectDomainName is the name of the domain where a project resides. - // It is used to identify the source domain of a project. - // ProjectDomainName can be used in addition to a ProjectName when scoping - // a user to a specific project. - ProjectDomainName string `yaml:"project_domain_name,omitempty" json:"project_domain_name,omitempty"` - - // ProjectDomainID is the name of the domain where a project resides. - // It is used to identify the source domain of a project. - // ProjectDomainID can be used in addition to a ProjectName when scoping - // a user to a specific project. - ProjectDomainID string `yaml:"project_domain_id,omitempty" json:"project_domain_id,omitempty"` - - // DomainName is the name of a domain which can be used to identify the - // source domain of either a user or a project. - // If UserDomainName and ProjectDomainName are not specified, then DomainName - // is used as a default choice. - // It can also be used be used to specify a domain-only scope. - DomainName string `yaml:"domain_name,omitempty" json:"domain_name,omitempty"` - - // DomainID is the unique ID of a domain which can be used to identify the - // source domain of eitehr a user or a project. - // If UserDomainID and ProjectDomainID are not specified, then DomainID is - // used as a default choice. - // It can also be used be used to specify a domain-only scope. - DomainID string `yaml:"domain_id,omitempty" json:"domain_id,omitempty"` - - // DefaultDomain is the domain ID to fall back on if no other domain has - // been specified and a domain is required for scope. - DefaultDomain string `yaml:"default_domain,omitempty" json:"default_domain,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 `yaml:"allow_reauth,omitempty" json:"allow_reauth,omitempty"` -} - -// Region represents a region included as part of cloud in clouds.yaml -// According to Python-based openstacksdk, this can be either a struct (as defined) -// or a plain string. Custom unmarshallers handle both cases. -type Region struct { - Name string `yaml:"name,omitempty" json:"name,omitempty"` - Values Cloud `yaml:"values,omitempty" json:"values,omitempty"` -} - -// UnmarshalJSON handles either a plain string acting as the Name property or -// a struct, mimicking the Python-based openstacksdk. -func (r *Region) UnmarshalJSON(data []byte) error { - var name string - if err := json.Unmarshal(data, &name); err == nil { - r.Name = name - return nil - } - - type region Region - var tmp region - if err := json.Unmarshal(data, &tmp); err != nil { - return err - } - r.Name = tmp.Name - r.Values = tmp.Values - - return nil -} - -// UnmarshalYAML handles either a plain string acting as the Name property or -// a struct, mimicking the Python-based openstacksdk. -func (r *Region) UnmarshalYAML(unmarshal func(interface{}) error) error { - var name string - if err := unmarshal(&name); err == nil { - r.Name = name - return nil - } - - type region Region - var tmp region - if err := unmarshal(&tmp); err != nil { - return err - } - r.Name = tmp.Name - r.Values = tmp.Values - - return nil -} - -// AuthType respresents a valid method of authentication. -type AuthType string - -const ( - // AuthPassword defines an unknown version of the password - AuthPassword AuthType = "password" - // AuthToken defined an unknown version of the token - AuthToken AuthType = "token" - - // AuthV2Password defines version 2 of the password - AuthV2Password AuthType = "v2password" - // AuthV2Token defines version 2 of the token - AuthV2Token AuthType = "v2token" - - // AuthV3Password defines version 3 of the password - AuthV3Password AuthType = "v3password" - // AuthV3Token defines version 3 of the token - AuthV3Token AuthType = "v3token" - - // AuthV3ApplicationCredential defines version 3 of the application credential - AuthV3ApplicationCredential AuthType = "v3applicationcredential" -) diff --git a/openstack/config/provider_client.go b/openstack/config/provider_client.go deleted file mode 100644 index a26b3511bd..0000000000 --- a/openstack/config/provider_client.go +++ /dev/null @@ -1,70 +0,0 @@ -package config - -import ( - "context" - "crypto/tls" - "net/http" - - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack" -) - -type options struct { - httpClient http.Client - tlsConfig *tls.Config -} - -// WithHTTPClient enables passing a custom http.Client to be used in the -// ProviderClient for authentication and for any further call, for example when -// using a ServiceClient derived from this ProviderClient. -func WithHTTPClient(httpClient http.Client) func(*options) { - return func(o *options) { - o.httpClient = httpClient - } -} - -// WithTLSConfig replaces the Transport of the default HTTP client (or of the -// HTTP client passed with WithHTTPClient) with a RoundTripper containing the -// given TLS config. -func WithTLSConfig(tlsConfig *tls.Config) func(*options) { - return func(o *options) { - o.tlsConfig = tlsConfig - } -} - -// NewProviderClient 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. -func NewProviderClient(ctx context.Context, authOptions gophercloud.AuthOptions, opts ...func(*options)) (*gophercloud.ProviderClient, error) { - var options options - for _, apply := range opts { - apply(&options) - } - - client, err := openstack.NewClient(authOptions.IdentityEndpoint) - if err != nil { - return nil, err - } - - if options.tlsConfig != nil { - transport := http.DefaultTransport.(*http.Transport).Clone() - transport.TLSClientConfig = options.tlsConfig - options.httpClient.Transport = transport - } - client.HTTPClient = options.httpClient - - err = openstack.AuthenticateWithContext(ctx, client, authOptions) - if err != nil { - return nil, err - } - return client, nil -} From 2e1a5f8891397c1007e3148eb31f0d3298b6686e Mon Sep 17 00:00:00 2001 From: Pierre Prinetti <pierreprinetti@redhat.com> Date: Wed, 6 Mar 2024 16:52:52 +0100 Subject: [PATCH 344/360] Revert "pager: Add WithContext functions" This reverts commit 2dbf414e62b9b5f7d4080ef02119c8112097384d. --- pagination/http.go | 12 +++--------- pagination/pager.go | 34 ++++++++++------------------------ 2 files changed, 13 insertions(+), 33 deletions(-) diff --git a/pagination/http.go b/pagination/http.go index 13b3925232..7845cda13b 100644 --- a/pagination/http.go +++ b/pagination/http.go @@ -1,7 +1,6 @@ package pagination import ( - "context" "encoding/json" "io/ioutil" "net/http" @@ -53,16 +52,11 @@ func PageResultFromParsed(resp *http.Response, body interface{}) PageResult { } } -// RequestWithContext performs an HTTP request and extracts the http.Response from the result. -func RequestWithContext(ctx context.Context, client *gophercloud.ServiceClient, headers map[string]string, url string) (*http.Response, error) { - return client.GetWithContext(ctx, url, nil, &gophercloud.RequestOpts{ +// Request performs an HTTP request and extracts the http.Response from the result. +func Request(client *gophercloud.ServiceClient, headers map[string]string, url string) (*http.Response, error) { + return client.Get(url, nil, &gophercloud.RequestOpts{ MoreHeaders: headers, OkCodes: []int{200, 204, 300}, KeepResponseBody: true, }) } - -// Request is a compatibility wrapper around RequestWithContext. -func Request(client *gophercloud.ServiceClient, headers map[string]string, url string) (*http.Response, error) { - return RequestWithContext(context.Background(), client, headers, url) -} diff --git a/pagination/pager.go b/pagination/pager.go index 8bc6680804..1dec2703eb 100644 --- a/pagination/pager.go +++ b/pagination/pager.go @@ -1,7 +1,6 @@ package pagination import ( - "context" "errors" "fmt" "net/http" @@ -70,8 +69,8 @@ func (p Pager) WithPageCreator(createPage func(r PageResult) Page) Pager { } } -func (p Pager) fetchNextPage(ctx context.Context, url string) (Page, error) { - resp, err := RequestWithContext(ctx, p.client, p.Headers, url) +func (p Pager) fetchNextPage(url string) (Page, error) { + resp, err := Request(p.client, p.Headers, url) if err != nil { return nil, err } @@ -84,10 +83,9 @@ func (p Pager) fetchNextPage(ctx context.Context, url string) (Page, error) { return p.createPage(remembered), nil } -// EachPageWithContext 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) EachPageWithContext(ctx context.Context, handler func(context.Context, Page) (bool, error)) error { +// EachPage iterates over each page returned by a Pager, yielding one at a time to a handler function. +// Return "false" from the handler to prematurely stop iterating. +func (p Pager) EachPage(handler func(Page) (bool, error)) error { if p.Err != nil { return p.Err } @@ -101,7 +99,7 @@ func (p Pager) EachPageWithContext(ctx context.Context, handler func(context.Con p.firstPage = nil } else { var err error - currentPage, err = p.fetchNextPage(ctx, currentURL) + currentPage, err = p.fetchNextPage(currentURL) if err != nil { return err } @@ -115,7 +113,7 @@ func (p Pager) EachPageWithContext(ctx context.Context, handler func(context.Con return nil } - ok, err := handler(ctx, currentPage) + ok, err := handler(currentPage) if err != nil { return err } @@ -133,16 +131,9 @@ func (p Pager) EachPageWithContext(ctx context.Context, handler func(context.Con } } -// EachPage is a compatibility wrapper around EachPageWithContext. -func (p Pager) EachPage(handler func(Page) (bool, error)) error { - return p.EachPageWithContext(context.Background(), func(_ context.Context, p Page) (bool, error) { - return handler(p) - }) -} - -// AllPagesWithContext returns all the pages from a `List` operation in a single page, +// 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) AllPagesWithContext(ctx context.Context) (Page, error) { +func (p Pager) AllPages() (Page, error) { if p.Err != nil { return nil, p.Err } @@ -152,7 +143,7 @@ func (p Pager) AllPagesWithContext(ctx context.Context) (Page, error) { var body reflect.Value // Grab a first page to ascertain the page body type. - firstPage, err := p.fetchNextPage(ctx, p.initialURL) + firstPage, err := p.fetchNextPage(p.initialURL) if err != nil { return nil, err } @@ -261,8 +252,3 @@ func (p Pager) AllPagesWithContext(ctx context.Context) (Page, error) { // `Extract*` methods will work. return page.Elem().Interface().(Page), err } - -// AllPages is a compatibility wrapper around AllPagesWithContext. -func (p Pager) AllPages() (Page, error) { - return p.AllPagesWithContext(context.Background()) -} From 4bd4c3832eca09554203475b75d8a67357c4a0f4 Mon Sep 17 00:00:00 2001 From: Pierre Prinetti <pierreprinetti@redhat.com> Date: Wed, 6 Mar 2024 16:53:11 +0100 Subject: [PATCH 345/360] Revert "client: Add WithContext functions" This reverts commit b8f25bcf0e63e86694f83f688a3fff660fcdd556. --- openstack/client.go | 123 +++++++++++++++++++------------------------- 1 file changed, 53 insertions(+), 70 deletions(-) diff --git a/openstack/client.go b/openstack/client.go index 8c6173a264..81c907c35b 100644 --- a/openstack/client.go +++ b/openstack/client.go @@ -1,7 +1,6 @@ package openstack import ( - "context" "fmt" "reflect" "strings" @@ -24,18 +23,20 @@ const ( v3 = "v3" ) -// NewClient prepares an unauthenticated ProviderClient instance. -// Most users will probably prefer using the AuthenticatedClient function -// instead. -// -// This is useful if you wish to explicitly control the version of the identity -// service that's used for authentication explicitly, for example. -// -// A basic example of using this would be: -// -// ao, err := openstack.AuthOptionsFromEnv() -// provider, err := openstack.NewClient(ao.IdentityEndpoint) -// client, err := openstack.NewIdentityV3(provider, gophercloud.EndpointOpts{}) +/* +NewClient prepares an unauthenticated ProviderClient instance. +Most users will probably prefer using the AuthenticatedClient function +instead. + +This is useful if you wish to explicitly control the version of the identity +service that's used for authentication explicitly, for example. + +A basic example of using this would be: + + ao, err := openstack.AuthOptionsFromEnv() + provider, err := openstack.NewClient(ao.IdentityEndpoint) + client, err := openstack.NewIdentityV3(provider, gophercloud.EndpointOpts{}) +*/ func NewClient(endpoint string) (*gophercloud.ProviderClient, error) { base, err := utils.BaseEndpoint(endpoint) if err != nil { @@ -53,45 +54,42 @@ func NewClient(endpoint string) (*gophercloud.ProviderClient, error) { return p, nil } -// AuthenticatedClientWithContext 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.AuthenticatedClientWithContext(ctx, ao) -// client, err := openstack.NewNetworkV2(provider, gophercloud.EndpointOpts{ -// Region: os.Getenv("OS_REGION_NAME"), -// }) -func AuthenticatedClientWithContext(ctx context.Context, options gophercloud.AuthOptions) (*gophercloud.ProviderClient, error) { +/* +AuthenticatedClient logs in to an OpenStack cloud found at the identity endpoint +specified by the options, acquires a token, and returns a Provider Client +instance that's ready to operate. + +If the full path to a versioned identity endpoint was specified (example: +http://example.com:5000/v3), that path will be used as the endpoint to query. + +If a versionless endpoint was specified (example: http://example.com:5000/), +the endpoint will be queried to determine which versions of the identity service +are available, then chooses the most recent or most supported version. + +Example: + + ao, err := openstack.AuthOptionsFromEnv() + provider, err := openstack.AuthenticatedClient(ao) + client, err := openstack.NewNetworkV2(provider, gophercloud.EndpointOpts{ + Region: os.Getenv("OS_REGION_NAME"), + }) +*/ +func AuthenticatedClient(options gophercloud.AuthOptions) (*gophercloud.ProviderClient, error) { client, err := NewClient(options.IdentityEndpoint) if err != nil { return nil, err } - err = AuthenticateWithContext(ctx, client, options) + err = Authenticate(client, options) if err != nil { return nil, err } return client, nil } -// AuthenticatedClient is a compatibility wrapper around AuthenticatedClientWithContext -func AuthenticatedClient(options gophercloud.AuthOptions) (*gophercloud.ProviderClient, error) { - return AuthenticatedClientWithContext(context.Background(), options) -} - -// AuthenticateWithContext authenticates or re-authenticates against the most -// recent identity service supported at the provided endpoint. -func AuthenticateWithContext(ctx context.Context, client *gophercloud.ProviderClient, options gophercloud.AuthOptions) error { +// Authenticate or re-authenticate against the most recent identity service +// supported at the provided endpoint. +func Authenticate(client *gophercloud.ProviderClient, options gophercloud.AuthOptions) error { versions := []*utils.Version{ {ID: v2, Priority: 20, Suffix: "/v2.0/"}, {ID: v3, Priority: 30, Suffix: "/v3/"}, @@ -104,31 +102,21 @@ func AuthenticateWithContext(ctx context.Context, client *gophercloud.ProviderCl switch chosen.ID { case v2: - return v2auth(ctx, client, endpoint, options, gophercloud.EndpointOpts{}) + return v2auth(client, endpoint, options, gophercloud.EndpointOpts{}) case v3: - return v3auth(ctx, client, endpoint, &options, gophercloud.EndpointOpts{}) + return v3auth(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) } } -// Authenticate is a compatibility wrapper around AuthenticateWithContext. -func Authenticate(client *gophercloud.ProviderClient, options gophercloud.AuthOptions) error { - return AuthenticateWithContext(context.Background(), client, options) -} - -// AuthenticateV2WithContext explicitly authenticates against the identity v2 endpoint. -func AuthenticateV2WithContext(ctx context.Context, client *gophercloud.ProviderClient, options gophercloud.AuthOptions, eo gophercloud.EndpointOpts) error { - return v2auth(ctx, client, "", options, eo) -} - -// AuthenticateV2 is a compatibility wrapper around AuthenticateV2WithContext. +// AuthenticateV2 explicitly authenticates against the identity v2 endpoint. func AuthenticateV2(client *gophercloud.ProviderClient, options gophercloud.AuthOptions, eo gophercloud.EndpointOpts) error { - return AuthenticateV2WithContext(context.Background(), client, options, eo) + return v2auth(client, "", options, eo) } -func v2auth(ctx context.Context, client *gophercloud.ProviderClient, endpoint string, options gophercloud.AuthOptions, eo gophercloud.EndpointOpts) error { +func v2auth(client *gophercloud.ProviderClient, endpoint string, options gophercloud.AuthOptions, eo gophercloud.EndpointOpts) error { v2Client, err := NewIdentityV2(client, eo) if err != nil { return err @@ -148,7 +136,7 @@ func v2auth(ctx context.Context, client *gophercloud.ProviderClient, endpoint st TokenID: options.TokenID, } - result := tokens2.CreateWithContext(ctx, v2Client, v2Opts) + result := tokens2.Create(v2Client, v2Opts) err = client.SetTokenAndAuthResult(result) if err != nil { @@ -171,7 +159,7 @@ func v2auth(ctx context.Context, client *gophercloud.ProviderClient, endpoint st tao := options tao.AllowReauth = false client.ReauthFunc = func() error { - err := v2auth(ctx, &tac, endpoint, tao, eo) + err := v2auth(&tac, endpoint, tao, eo) if err != nil { return err } @@ -186,17 +174,12 @@ func v2auth(ctx context.Context, client *gophercloud.ProviderClient, endpoint st return nil } -// AuthenticateV3WithContext explicitly authenticates against the identity v3 service. -func AuthenticateV3WithContext(ctx context.Context, client *gophercloud.ProviderClient, options tokens3.AuthOptionsBuilder, eo gophercloud.EndpointOpts) error { - return v3auth(ctx, client, "", options, eo) -} - -// AuthenticateV3 is a compatibility wrapper around AuthenticateV3WithContext +// AuthenticateV3 explicitly authenticates against the identity v3 service. func AuthenticateV3(client *gophercloud.ProviderClient, options tokens3.AuthOptionsBuilder, eo gophercloud.EndpointOpts) error { - return AuthenticateV3WithContext(context.Background(), client, options, eo) + return v3auth(client, "", options, eo) } -func v3auth(ctx context.Context, client *gophercloud.ProviderClient, endpoint string, opts tokens3.AuthOptionsBuilder, eo gophercloud.EndpointOpts) error { +func v3auth(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 { @@ -246,11 +229,11 @@ func v3auth(ctx context.Context, client *gophercloud.ProviderClient, endpoint st var result tokens3.CreateResult switch opts.(type) { case *ec2tokens.AuthOptions: - result = ec2tokens.CreateWithContext(ctx, v3Client, opts) + result = ec2tokens.Create(v3Client, opts) case *oauth1.AuthOptions: - result = oauth1.CreateWithContext(ctx, v3Client, opts) + result = oauth1.Create(v3Client, opts) default: - result = tokens3.CreateWithContext(ctx, v3Client, opts) + result = tokens3.Create(v3Client, opts) } err = client.SetTokenAndAuthResult(result) @@ -294,7 +277,7 @@ func v3auth(ctx context.Context, client *gophercloud.ProviderClient, endpoint st tao = opts } client.ReauthFunc = func() error { - err := v3auth(ctx, &tac, endpoint, tao, eo) + err := v3auth(&tac, endpoint, tao, eo) if err != nil { return err } From 6046211a881759d50fa63d922b47ceefbc908383 Mon Sep 17 00:00:00 2001 From: Pierre Prinetti <pierreprinetti@redhat.com> Date: Wed, 6 Mar 2024 16:53:19 +0100 Subject: [PATCH 346/360] Revert "tokens: Add WithContext functions" This reverts commit b0d7555b64cf64871644bdb3e3d8d97b2d65fc6d. --- openstack/identity/v2/tokens/requests.go | 26 +--- .../v3/extensions/ec2tokens/requests.go | 26 +--- .../identity/v3/extensions/oauth1/requests.go | 124 +++++------------- openstack/identity/v3/tokens/requests.go | 50 ++----- 4 files changed, 60 insertions(+), 166 deletions(-) diff --git a/openstack/identity/v2/tokens/requests.go b/openstack/identity/v2/tokens/requests.go index 67c04fced2..84f16c3fc2 100644 --- a/openstack/identity/v2/tokens/requests.go +++ b/openstack/identity/v2/tokens/requests.go @@ -1,10 +1,6 @@ package tokens -import ( - "context" - - "github.com/gophercloud/gophercloud" -) +import "github.com/gophercloud/gophercloud" // PasswordCredentialsV2 represents the required options to authenticate // with a username and password. @@ -81,17 +77,17 @@ func (opts AuthOptions) ToTokenV2CreateMap() (map[string]interface{}, error) { return b, nil } -// CreateWithContext authenticates to the identity service and attempts to acquire a Token. +// 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 CreateWithContext(ctx context.Context, client *gophercloud.ServiceClient, auth AuthOptionsBuilder) (r CreateResult) { +func Create(client *gophercloud.ServiceClient, auth AuthOptionsBuilder) (r CreateResult) { b, err := auth.ToTokenV2CreateMap() if err != nil { r.Err = err return } - resp, err := client.PostWithContext(ctx, CreateURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(CreateURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 203}, OmitHeaders: []string{"X-Auth-Token"}, }) @@ -99,21 +95,11 @@ func CreateWithContext(ctx context.Context, client *gophercloud.ServiceClient, a return } -// Create is a compatibility wrapper around CreateWithContext -func Create(client *gophercloud.ServiceClient, auth AuthOptionsBuilder) (r CreateResult) { - return CreateWithContext(context.Background(), client, auth) -} - -// GetWithContext validates and retrieves information for user's token. -func GetWithContext(ctx context.Context, client *gophercloud.ServiceClient, token string) (r GetResult) { +// Get validates and retrieves information for user's token. +func Get(client *gophercloud.ServiceClient, token string) (r GetResult) { resp, err := client.Get(GetURL(client, token), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 203}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } - -// Get is a compatibility wrapper around GetWithContext -func Get(client *gophercloud.ServiceClient, token string) (r GetResult) { - return GetWithContext(context.Background(), client, token) -} diff --git a/openstack/identity/v3/extensions/ec2tokens/requests.go b/openstack/identity/v3/extensions/ec2tokens/requests.go index b5b5350c54..32ba0e621d 100644 --- a/openstack/identity/v3/extensions/ec2tokens/requests.go +++ b/openstack/identity/v3/extensions/ec2tokens/requests.go @@ -1,7 +1,6 @@ package ec2tokens import ( - "context" "crypto/hmac" "crypto/sha1" "crypto/sha256" @@ -288,8 +287,8 @@ func (opts *AuthOptions) ToTokenV3CreateMap(map[string]interface{}) (map[string] return b, nil } -// CreateWithContext authenticates and either generates a new token from EC2 credentials -func CreateWithContext(ctx context.Context, c *gophercloud.ServiceClient, opts tokens.AuthOptionsBuilder) (r tokens.CreateResult) { +// Create authenticates and either generates a new token from EC2 credentials +func Create(c *gophercloud.ServiceClient, opts tokens.AuthOptionsBuilder) (r tokens.CreateResult) { b, err := opts.ToTokenV3CreateMap(nil) if err != nil { r.Err = err @@ -299,7 +298,7 @@ func CreateWithContext(ctx context.Context, c *gophercloud.ServiceClient, opts t // delete "token" element, since it is used in s3tokens deleteBodyElements(b, "token") - resp, err := c.PostWithContext(ctx, ec2tokensURL(c), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Post(ec2tokensURL(c), b, &r.Body, &gophercloud.RequestOpts{ MoreHeaders: map[string]string{"X-Auth-Token": ""}, OkCodes: []int{200}, }) @@ -307,15 +306,9 @@ func CreateWithContext(ctx context.Context, c *gophercloud.ServiceClient, opts t return } -// Create is a compatibility wrapper around CreateWithContext -func Create(c *gophercloud.ServiceClient, opts tokens.AuthOptionsBuilder) (r tokens.CreateResult) { - return CreateWithContext(context.Background(), c, opts) -} - -// ValidateS3TokenWithContext authenticates an S3 request using EC2 -// credentials. Doesn't generate a new token ID, but returns a -// tokens.CreateResult. -func ValidateS3TokenWithContext(ctx context.Context, c *gophercloud.ServiceClient, opts tokens.AuthOptionsBuilder) (r tokens.CreateResult) { +// ValidateS3Token authenticates an S3 request using EC2 credentials. Doesn't +// generate a new token ID, but returns a tokens.CreateResult. +func ValidateS3Token(c *gophercloud.ServiceClient, opts tokens.AuthOptionsBuilder) (r tokens.CreateResult) { b, err := opts.ToTokenV3CreateMap(nil) if err != nil { r.Err = err @@ -325,7 +318,7 @@ func ValidateS3TokenWithContext(ctx context.Context, c *gophercloud.ServiceClien // delete unused element, since it is used in ec2tokens only deleteBodyElements(b, "body_hash", "headers", "host", "params", "path", "verb") - resp, err := c.PostWithContext(ctx, s3tokensURL(c), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Post(s3tokensURL(c), b, &r.Body, &gophercloud.RequestOpts{ MoreHeaders: map[string]string{"X-Auth-Token": ""}, OkCodes: []int{200}, }) @@ -333,11 +326,6 @@ func ValidateS3TokenWithContext(ctx context.Context, c *gophercloud.ServiceClien return } -// ValidateS3Token is a compatibility wrapper around ValidateS3TokenWithContext -func ValidateS3Token(c *gophercloud.ServiceClient, opts tokens.AuthOptionsBuilder) (r tokens.CreateResult) { - return ValidateS3TokenWithContext(context.Background(), c, opts) -} - // The following are small helper functions used to help build the signature. // sumHMAC1 is a func to implement the HMAC SHA1 signature method. diff --git a/openstack/identity/v3/extensions/oauth1/requests.go b/openstack/identity/v3/extensions/oauth1/requests.go index 9921056320..028b5a45bd 100644 --- a/openstack/identity/v3/extensions/oauth1/requests.go +++ b/openstack/identity/v3/extensions/oauth1/requests.go @@ -1,7 +1,6 @@ package oauth1 import ( - "context" "crypto/hmac" "crypto/sha1" "encoding/base64" @@ -134,9 +133,9 @@ func (opts AuthOptions) ToTokenV3CreateMap(map[string]interface{}) (map[string]i return gophercloud.BuildRequestBody(req, "") } -// CreateWithContext authenticates and either generates a new OpenStack token -// from an OAuth1 token. -func CreateWithContext(ctx context.Context, client *gophercloud.ServiceClient, opts tokens.AuthOptionsBuilder) (r tokens.CreateResult) { +// Create authenticates and either generates a new OpenStack token from an +// OAuth1 token. +func Create(client *gophercloud.ServiceClient, opts tokens.AuthOptionsBuilder) (r tokens.CreateResult) { b, err := opts.ToTokenV3CreateMap(nil) if err != nil { r.Err = err @@ -154,7 +153,7 @@ func CreateWithContext(ctx context.Context, client *gophercloud.ServiceClient, o return } - resp, err := client.PostWithContext(ctx, authURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(authURL(client), b, &r.Body, &gophercloud.RequestOpts{ MoreHeaders: h, OkCodes: []int{201}, }) @@ -162,11 +161,6 @@ func CreateWithContext(ctx context.Context, client *gophercloud.ServiceClient, o return } -// Create is a compatibility wrapper around CreateWithContext. -func Create(client *gophercloud.ServiceClient, opts tokens.AuthOptionsBuilder) (r tokens.CreateResult) { - return CreateWithContext(context.Background(), client, opts) -} - // CreateConsumerOptsBuilder allows extensions to add additional parameters to // the CreateConsumer request. type CreateConsumerOptsBuilder interface { @@ -184,37 +178,27 @@ func (opts CreateConsumerOpts) ToOAuth1CreateConsumerMap() (map[string]interface return gophercloud.BuildRequestBody(opts, "consumer") } -// CreateConsumerWithContext creates a new Consumer. -func CreateConsumerWithContext(ctx context.Context, client *gophercloud.ServiceClient, opts CreateConsumerOptsBuilder) (r CreateConsumerResult) { +// Create creates a new Consumer. +func CreateConsumer(client *gophercloud.ServiceClient, opts CreateConsumerOptsBuilder) (r CreateConsumerResult) { b, err := opts.ToOAuth1CreateConsumerMap() if err != nil { r.Err = err return } - resp, err := client.PostWithContext(ctx, consumersURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(consumersURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } -// CreateConsumer is a compatibility wrapper around CreateConsumerWithContext. -func CreateConsumer(client *gophercloud.ServiceClient, opts CreateConsumerOptsBuilder) (r CreateConsumerResult) { - return CreateConsumerWithContext(context.Background(), client, opts) -} - -// DeleteConsumerWithContext deletes a Consumer. -func DeleteConsumerWithContext(ctx context.Context, client *gophercloud.ServiceClient, id string) (r DeleteConsumerResult) { - resp, err := client.DeleteWithContext(ctx, consumerURL(client, id), nil) +// Delete deletes a Consumer. +func DeleteConsumer(client *gophercloud.ServiceClient, id string) (r DeleteConsumerResult) { + resp, err := client.Delete(consumerURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } -// DeleteConsumer is a compatibility wrapper around DeleteConsumerWithContext. -func DeleteConsumer(client *gophercloud.ServiceClient, id string) (r DeleteConsumerResult) { - return DeleteConsumerWithContext(context.Background(), client, id) -} - // List enumerates Consumers. func ListConsumers(client *gophercloud.ServiceClient) pagination.Pager { return pagination.NewPager(client, consumersURL(client), func(r pagination.PageResult) pagination.Page { @@ -222,18 +206,13 @@ func ListConsumers(client *gophercloud.ServiceClient) pagination.Pager { }) } -// GetConsumerWithContext retrieves details on a single Consumer by ID. -func GetConsumerWithContext(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetConsumerResult) { - resp, err := client.GetWithContext(ctx, consumerURL(client, id), &r.Body, nil) +// GetConsumer retrieves details on a single Consumer by ID. +func GetConsumer(client *gophercloud.ServiceClient, id string) (r GetConsumerResult) { + resp, err := client.Get(consumerURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } -// GetConsumer is a compatibility wrapper around GetConsumerWithContext. -func GetConsumer(client *gophercloud.ServiceClient, id string) (r GetConsumerResult) { - return GetConsumerWithContext(context.Background(), client, id) -} - // UpdateConsumerOpts provides options used to update a consumer. type UpdateConsumerOpts struct { // Description is the consumer description. @@ -246,25 +225,20 @@ func (opts UpdateConsumerOpts) ToOAuth1UpdateConsumerMap() (map[string]interface return gophercloud.BuildRequestBody(opts, "consumer") } -// UpdateConsumerWithContext updates an existing Consumer. -func UpdateConsumerWithContext(ctx context.Context, client *gophercloud.ServiceClient, id string, opts UpdateConsumerOpts) (r UpdateConsumerResult) { +// UpdateConsumer updates an existing Consumer. +func UpdateConsumer(client *gophercloud.ServiceClient, id string, opts UpdateConsumerOpts) (r UpdateConsumerResult) { b, err := opts.ToOAuth1UpdateConsumerMap() if err != nil { r.Err = err return } - resp, err := client.PatchWithContext(ctx, consumerURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Patch(consumerURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } -// UpdateConsumer is a compatibility wrapper around UpdateConsumerWithContext. -func UpdateConsumer(client *gophercloud.ServiceClient, id string, opts UpdateConsumerOpts) (r UpdateConsumerResult) { - return UpdateConsumerWithContext(context.Background(), client, id, opts) -} - // RequestTokenOptsBuilder allows extensions to add additional parameters to the // RequestToken request. type RequestTokenOptsBuilder interface { @@ -323,15 +297,15 @@ func (opts RequestTokenOpts) ToOAuth1RequestTokenHeaders(method, u string) (map[ return h, nil } -// RequestTokenWithContext requests an unauthorized OAuth1 Token. -func RequestTokenWithContext(ctx context.Context, client *gophercloud.ServiceClient, opts RequestTokenOptsBuilder) (r TokenResult) { +// RequestToken requests an unauthorized OAuth1 Token. +func RequestToken(client *gophercloud.ServiceClient, opts RequestTokenOptsBuilder) (r TokenResult) { h, err := opts.ToOAuth1RequestTokenHeaders("POST", requestTokenURL(client)) if err != nil { r.Err = err return } - resp, err := client.PostWithContext(ctx, requestTokenURL(client), nil, nil, &gophercloud.RequestOpts{ + resp, err := client.Post(requestTokenURL(client), nil, nil, &gophercloud.RequestOpts{ MoreHeaders: h, OkCodes: []int{201}, KeepResponseBody: true, @@ -349,11 +323,6 @@ func RequestTokenWithContext(ctx context.Context, client *gophercloud.ServiceCli return } -// RequestToken is a compatibility wrapper around RequestTokenWithContext. -func RequestToken(client *gophercloud.ServiceClient, opts RequestTokenOptsBuilder) (r TokenResult) { - return RequestTokenWithContext(context.Background(), client, opts) -} - // AuthorizeTokenOptsBuilder allows extensions to add additional parameters to // the AuthorizeToken request. type AuthorizeTokenOptsBuilder interface { @@ -382,25 +351,20 @@ func (opts AuthorizeTokenOpts) ToOAuth1AuthorizeTokenMap() (map[string]interface return gophercloud.BuildRequestBody(opts, "") } -// AuthorizeTokenWithContext authorizes an unauthorized consumer token. -func AuthorizeTokenWithContext(ctx context.Context, client *gophercloud.ServiceClient, id string, opts AuthorizeTokenOptsBuilder) (r AuthorizeTokenResult) { +// AuthorizeToken authorizes an unauthorized consumer token. +func AuthorizeToken(client *gophercloud.ServiceClient, id string, opts AuthorizeTokenOptsBuilder) (r AuthorizeTokenResult) { b, err := opts.ToOAuth1AuthorizeTokenMap() if err != nil { r.Err = err return } - resp, err := client.PutWithContext(ctx, authorizeTokenURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Put(authorizeTokenURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } -// AuthorizeToken is a compatibility wrapper around AuthorizeTokenWithContext. -func AuthorizeToken(client *gophercloud.ServiceClient, id string, opts AuthorizeTokenOptsBuilder) (r AuthorizeTokenResult) { - return AuthorizeTokenWithContext(context.Background(), client, id, opts) -} - // CreateAccessTokenOptsBuilder allows extensions to add additional parameters // to the CreateAccessToken request. type CreateAccessTokenOptsBuilder interface { @@ -461,15 +425,15 @@ func (opts CreateAccessTokenOpts) ToOAuth1CreateAccessTokenHeaders(method, u str return headers, nil } -// CreateAccessTokenWithContext creates a new OAuth1 Access Token -func CreateAccessTokenWithContext(ctx context.Context, client *gophercloud.ServiceClient, opts CreateAccessTokenOptsBuilder) (r TokenResult) { +// CreateAccessToken creates a new OAuth1 Access Token +func CreateAccessToken(client *gophercloud.ServiceClient, opts CreateAccessTokenOptsBuilder) (r TokenResult) { h, err := opts.ToOAuth1CreateAccessTokenHeaders("POST", createAccessTokenURL(client)) if err != nil { r.Err = err return } - resp, err := client.PostWithContext(ctx, createAccessTokenURL(client), nil, nil, &gophercloud.RequestOpts{ + resp, err := client.Post(createAccessTokenURL(client), nil, nil, &gophercloud.RequestOpts{ MoreHeaders: h, OkCodes: []int{201}, KeepResponseBody: true, @@ -487,33 +451,18 @@ func CreateAccessTokenWithContext(ctx context.Context, client *gophercloud.Servi return } -// CreateAccessToken is a compatibility wrapper around CreateAccessTokenWithContext. -func CreateAccessToken(client *gophercloud.ServiceClient, opts CreateAccessTokenOptsBuilder) (r TokenResult) { - return CreateAccessTokenWithContext(context.Background(), client, opts) -} - -// GetAccessTokenWithContext retrieves details on a single OAuth1 access token by an ID. -func GetAccessTokenWithContext(ctx context.Context, client *gophercloud.ServiceClient, userID string, id string) (r GetAccessTokenResult) { - resp, err := client.GetWithContext(ctx, userAccessTokenURL(client, userID, id), &r.Body, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// GetAccessToken is a compatibility wrapper around GetAccessTokenWithContext. +// GetAccessToken retrieves details on a single OAuth1 access token by an ID. func GetAccessToken(client *gophercloud.ServiceClient, userID string, id string) (r GetAccessTokenResult) { - return GetAccessTokenWithContext(context.Background(), client, userID, id) -} - -// RevokeAccessTokenWithContext revokes an OAuth1 access token. -func RevokeAccessTokenWithContext(ctx context.Context, client *gophercloud.ServiceClient, userID string, id string) (r RevokeAccessTokenResult) { - resp, err := client.DeleteWithContext(ctx, userAccessTokenURL(client, userID, id), nil) + resp, err := client.Get(userAccessTokenURL(client, userID, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } -// RevokeAccessToken is a compatibility wrapper around RevokeAccessTokenWithContext. +// RevokeAccessToken revokes an OAuth1 access token. func RevokeAccessToken(client *gophercloud.ServiceClient, userID string, id string) (r RevokeAccessTokenResult) { - return RevokeAccessTokenWithContext(context.Background(), client, userID, id) + resp, err := client.Delete(userAccessTokenURL(client, userID, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return } // ListAccessTokens enumerates authorized access tokens. @@ -532,19 +481,14 @@ func ListAccessTokenRoles(client *gophercloud.ServiceClient, userID string, id s }) } -// GetAccessTokenRoleWithContext retrieves details on a single OAuth1 access token role by +// GetAccessTokenRole retrieves details on a single OAuth1 access token role by // an ID. -func GetAccessTokenRoleWithContext(ctx context.Context, client *gophercloud.ServiceClient, userID string, id string, roleID string) (r GetAccessTokenRoleResult) { - resp, err := client.GetWithContext(ctx, userAccessTokenRoleURL(client, userID, id, roleID), &r.Body, nil) +func GetAccessTokenRole(client *gophercloud.ServiceClient, userID string, id string, roleID string) (r GetAccessTokenRoleResult) { + resp, err := client.Get(userAccessTokenRoleURL(client, userID, id, roleID), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } -// GetAccessTokenRole is a compatibility wrapper around GetAccessTokenRoleWithContext. -func GetAccessTokenRole(client *gophercloud.ServiceClient, userID string, id string, roleID string) (r GetAccessTokenRoleResult) { - return GetAccessTokenRoleWithContext(context.Background(), client, userID, id, roleID) -} - // The following are small helper functions used to help build the signature. // buildOAuth1QueryString builds a URLEncoded parameters string specific for diff --git a/openstack/identity/v3/tokens/requests.go b/openstack/identity/v3/tokens/requests.go index d5b2fe83fd..1af55d8137 100644 --- a/openstack/identity/v3/tokens/requests.go +++ b/openstack/identity/v3/tokens/requests.go @@ -1,10 +1,6 @@ package tokens -import ( - "context" - - "github.com/gophercloud/gophercloud" -) +import "github.com/gophercloud/gophercloud" // Scope allows a created token to be limited to a specific domain or project. type Scope struct { @@ -123,9 +119,9 @@ func subjectTokenHeaders(subjectToken string) map[string]string { } } -// CreateWithContext authenticates and either generates a new token, or changes the Scope +// Create authenticates and either generates a new token, or changes the Scope // of an existing token. -func CreateWithContext(ctx context.Context, c *gophercloud.ServiceClient, opts AuthOptionsBuilder) (r CreateResult) { +func Create(c *gophercloud.ServiceClient, opts AuthOptionsBuilder) (r CreateResult) { scope, err := opts.ToTokenV3ScopeMap() if err != nil { r.Err = err @@ -138,21 +134,16 @@ func CreateWithContext(ctx context.Context, c *gophercloud.ServiceClient, opts A return } - resp, err := c.PostWithContext(ctx, tokenURL(c), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Post(tokenURL(c), b, &r.Body, &gophercloud.RequestOpts{ OmitHeaders: []string{"X-Auth-Token"}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } -// Create is a compatibility wrapper around CreateWithContext -func Create(c *gophercloud.ServiceClient, opts AuthOptionsBuilder) (r CreateResult) { - return CreateWithContext(context.Background(), c, opts) -} - -// GetGetWithContext validates and retrieves information about another token. -func GetWithContext(ctx context.Context, c *gophercloud.ServiceClient, token string) (r GetResult) { - resp, err := c.GetWithContext(ctx, tokenURL(c), &r.Body, &gophercloud.RequestOpts{ +// Get validates and retrieves information about another token. +func Get(c *gophercloud.ServiceClient, token string) (r GetResult) { + resp, err := c.Get(tokenURL(c), &r.Body, &gophercloud.RequestOpts{ MoreHeaders: subjectTokenHeaders(token), OkCodes: []int{200, 203}, }) @@ -160,14 +151,9 @@ func GetWithContext(ctx context.Context, c *gophercloud.ServiceClient, token str return } -// Get is a compatibility wrapper around GetWithContext -func Get(c *gophercloud.ServiceClient, token string) (r GetResult) { - return GetWithContext(context.Background(), c, token) -} - -// ValidateWithContext determines if a specified token is valid or not. -func ValidateWithContext(ctx context.Context, c *gophercloud.ServiceClient, token string) (bool, error) { - resp, err := c.HeadWithContext(ctx, tokenURL(c), &gophercloud.RequestOpts{ +// Validate determines if a specified token is valid or not. +func Validate(c *gophercloud.ServiceClient, token string) (bool, error) { + resp, err := c.Head(tokenURL(c), &gophercloud.RequestOpts{ MoreHeaders: subjectTokenHeaders(token), OkCodes: []int{200, 204, 404}, }) @@ -178,21 +164,11 @@ func ValidateWithContext(ctx context.Context, c *gophercloud.ServiceClient, toke return resp.StatusCode == 200 || resp.StatusCode == 204, nil } -// Validate is a compatibility wrapper around ValidateWithContext -func Validate(c *gophercloud.ServiceClient, token string) (bool, error) { - return ValidateWithContext(context.Background(), c, token) -} - -// RevokeWithContext immediately makes specified token invalid. -func RevokeWithContext(ctx context.Context, c *gophercloud.ServiceClient, token string) (r RevokeResult) { - resp, err := c.DeleteWithContext(ctx, tokenURL(c), &gophercloud.RequestOpts{ +// Revoke immediately makes specified token invalid. +func Revoke(c *gophercloud.ServiceClient, token string) (r RevokeResult) { + resp, err := c.Delete(tokenURL(c), &gophercloud.RequestOpts{ MoreHeaders: subjectTokenHeaders(token), }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } - -// Revoke is a compatibility wrapper around RevokeWithContext -func Revoke(c *gophercloud.ServiceClient, token string) (r RevokeResult) { - return RevokeWithContext(context.Background(), c, token) -} From 322ccdf71b3ddd15c0f554a101822a42648624f8 Mon Sep 17 00:00:00 2001 From: Pierre Prinetti <pierreprinetti@redhat.com> Date: Wed, 6 Mar 2024 17:30:30 +0100 Subject: [PATCH 347/360] Revert "Context-aware methods to ProviderClient and ServiceClient" This reverts commit ef06ea2bef3b883355e3d109bbf4c48c4f40e8e0. --- internal/ctxt/context.go | 739 ------------------------------------ internal/ctxt/merge.go | 52 --- internal/ctxt/merge_test.go | 133 ------- provider_client.go | 45 +-- service_client.go | 76 +--- 5 files changed, 37 insertions(+), 1008 deletions(-) delete mode 100644 internal/ctxt/context.go delete mode 100644 internal/ctxt/merge.go delete mode 100644 internal/ctxt/merge_test.go diff --git a/internal/ctxt/context.go b/internal/ctxt/context.go deleted file mode 100644 index 2352583714..0000000000 --- a/internal/ctxt/context.go +++ /dev/null @@ -1,739 +0,0 @@ -package ctxt - -// This file is package context of the standard library that ships with Go -// v1.21.6. It has been vendored to import AfterFunc. -// -// Changes made to the original file: -// * replace "internal/reflectlite" with "reflect" in the imports -// * replace "any" with "interface{}" -// * remove the atomic.Int32 type that only exists for testing and is not -// compatible with Go v1.14. -// -// https://cs.opensource.google/go/go/+/refs/tags/go1.21.6:src/context/context.go - -import ( - "errors" - reflectlite "reflect" - "sync" - "sync/atomic" - "time" -) - -// A Context carries a deadline, a cancellation signal, and other values across -// API boundaries. -// -// Context's methods may be called by multiple goroutines simultaneously. -type Context interface { - // Deadline returns the time when work done on behalf of this context - // should be canceled. Deadline returns ok==false when no deadline is - // set. Successive calls to Deadline return the same results. - Deadline() (deadline time.Time, ok bool) - - // Done returns a channel that's closed when work done on behalf of this - // context should be canceled. Done may return nil if this context can - // never be canceled. Successive calls to Done return the same value. - // The close of the Done channel may happen asynchronously, - // after the cancel function returns. - // - // WithCancel arranges for Done to be closed when cancel is called; - // WithDeadline arranges for Done to be closed when the deadline - // expires; WithTimeout arranges for Done to be closed when the timeout - // elapses. - // - // Done is provided for use in select statements: - // - // // Stream generates values with DoSomething and sends them to out - // // until DoSomething returns an error or ctx.Done is closed. - // func Stream(ctx context.Context, out chan<- Value) error { - // for { - // v, err := DoSomething(ctx) - // if err != nil { - // return err - // } - // select { - // case <-ctx.Done(): - // return ctx.Err() - // case out <- v: - // } - // } - // } - // - // See https://blog.golang.org/pipelines for more examples of how to use - // a Done channel for cancellation. - Done() <-chan struct{} - - // If Done is not yet closed, Err returns nil. - // If Done is closed, Err returns a non-nil error explaining why: - // Canceled if the context was canceled - // or DeadlineExceeded if the context's deadline passed. - // After Err returns a non-nil error, successive calls to Err return the same error. - Err() error - - // Value returns the value associated with this context for key, or nil - // if no value is associated with key. Successive calls to Value with - // the same key returns the same result. - // - // Use context values only for request-scoped data that transits - // processes and API boundaries, not for passing optional parameters to - // functions. - // - // A key identifies a specific value in a Context. Functions that wish - // to store values in Context typically allocate a key in a global - // variable then use that key as the argument to context.WithValue and - // Context.Value. A key can be any type that supports equality; - // packages should define keys as an unexported type to avoid - // collisions. - // - // Packages that define a Context key should provide type-safe accessors - // for the values stored using that key: - // - // // Package user defines a User type that's stored in Contexts. - // package user - // - // import "context" - // - // // User is the type of value stored in the Contexts. - // type User struct {...} - // - // // key is an unexported type for keys defined in this package. - // // This prevents collisions with keys defined in other packages. - // type key int - // - // // userKey is the key for user.User values in Contexts. It is - // // unexported; clients use user.NewContext and user.FromContext - // // instead of using this key directly. - // var userKey key - // - // // NewContext returns a new Context that carries value u. - // func NewContext(ctx context.Context, u *User) context.Context { - // return context.WithValue(ctx, userKey, u) - // } - // - // // FromContext returns the User value stored in ctx, if any. - // func FromContext(ctx context.Context) (*User, bool) { - // u, ok := ctx.Value(userKey).(*User) - // return u, ok - // } - Value(key interface{}) interface{} -} - -// Canceled is the error returned by [Context.Err] when the context is canceled. -var Canceled = errors.New("context canceled") - -// DeadlineExceeded is the error returned by [Context.Err] when the context's -// deadline passes. -var DeadlineExceeded error = deadlineExceededError{} - -type deadlineExceededError struct{} - -func (deadlineExceededError) Error() string { return "context deadline exceeded" } -func (deadlineExceededError) Timeout() bool { return true } -func (deadlineExceededError) Temporary() bool { return true } - -// An emptyCtx is never canceled, has no values, and has no deadline. -// It is the common base of backgroundCtx and todoCtx. -type emptyCtx struct{} - -func (emptyCtx) Deadline() (deadline time.Time, ok bool) { - return -} - -func (emptyCtx) Done() <-chan struct{} { - return nil -} - -func (emptyCtx) Err() error { - return nil -} - -func (emptyCtx) Value(key interface{}) interface{} { - return nil -} - -type backgroundCtx struct{ emptyCtx } - -func (backgroundCtx) String() string { - return "context.Background" -} - -type todoCtx struct{ emptyCtx } - -func (todoCtx) String() string { - return "context.TODO" -} - -// Background returns a non-nil, empty [Context]. It is never canceled, has no -// values, and has no deadline. It is typically used by the main function, -// initialization, and tests, and as the top-level Context for incoming -// requests. -func Background() Context { - return backgroundCtx{} -} - -// TODO returns a non-nil, empty [Context]. Code should use context.TODO when -// it's unclear which Context to use or it is not yet available (because the -// surrounding function has not yet been extended to accept a Context -// parameter). -func TODO() Context { - return todoCtx{} -} - -// A CancelFunc tells an operation to abandon its work. -// A CancelFunc does not wait for the work to stop. -// A CancelFunc may be called by multiple goroutines simultaneously. -// After the first call, subsequent calls to a CancelFunc do nothing. -type CancelFunc func() - -// WithCancel returns a copy of parent with a new Done channel. The returned -// context's Done channel is closed when the returned cancel function is called -// or when the parent context's Done channel is closed, whichever happens first. -// -// Canceling this context releases resources associated with it, so code should -// call cancel as soon as the operations running in this Context complete. -func WithCancel(parent Context) (ctx Context, cancel CancelFunc) { - c := withCancel(parent) - return c, func() { c.cancel(true, Canceled, nil) } -} - -// A CancelCauseFunc behaves like a [CancelFunc] but additionally sets the cancellation cause. -// This cause can be retrieved by calling [Cause] on the canceled Context or on -// any of its derived Contexts. -// -// If the context has already been canceled, CancelCauseFunc does not set the cause. -// For example, if childContext is derived from parentContext: -// - if parentContext is canceled with cause1 before childContext is canceled with cause2, -// then Cause(parentContext) == Cause(childContext) == cause1 -// - if childContext is canceled with cause2 before parentContext is canceled with cause1, -// then Cause(parentContext) == cause1 and Cause(childContext) == cause2 -type CancelCauseFunc func(cause error) - -// WithCancelCause behaves like [WithCancel] but returns a [CancelCauseFunc] instead of a [CancelFunc]. -// Calling cancel with a non-nil error (the "cause") records that error in ctx; -// it can then be retrieved using Cause(ctx). -// Calling cancel with nil sets the cause to Canceled. -// -// Example use: -// -// ctx, cancel := context.WithCancelCause(parent) -// cancel(myError) -// ctx.Err() // returns context.Canceled -// context.Cause(ctx) // returns myError -func WithCancelCause(parent Context) (ctx Context, cancel CancelCauseFunc) { - c := withCancel(parent) - return c, func(cause error) { c.cancel(true, Canceled, cause) } -} - -func withCancel(parent Context) *cancelCtx { - if parent == nil { - panic("cannot create context from nil parent") - } - c := &cancelCtx{} - c.propagateCancel(parent, c) - return c -} - -// Cause returns a non-nil error explaining why c was canceled. -// The first cancellation of c or one of its parents sets the cause. -// If that cancellation happened via a call to CancelCauseFunc(err), -// then [Cause] returns err. -// Otherwise Cause(c) returns the same value as c.Err(). -// Cause returns nil if c has not been canceled yet. -func Cause(c Context) error { - if cc, ok := c.Value(&cancelCtxKey).(*cancelCtx); ok { - cc.mu.Lock() - defer cc.mu.Unlock() - return cc.cause - } - return nil -} - -// AfterFunc arranges to call f in its own goroutine after ctx is done -// (cancelled or timed out). -// If ctx is already done, AfterFunc calls f immediately in its own goroutine. -// -// Multiple calls to AfterFunc on a context operate independently; -// one does not replace another. -// -// Calling the returned stop function stops the association of ctx with f. -// It returns true if the call stopped f from being run. -// If stop returns false, -// either the context is done and f has been started in its own goroutine; -// or f was already stopped. -// The stop function does not wait for f to complete before returning. -// If the caller needs to know whether f is completed, -// it must coordinate with f explicitly. -// -// If ctx has a "AfterFunc(func()) func() bool" method, -// AfterFunc will use it to schedule the call. -func AfterFunc(ctx Context, f func()) (stop func() bool) { - a := &afterFuncCtx{ - f: f, - } - a.cancelCtx.propagateCancel(ctx, a) - return func() bool { - stopped := false - a.once.Do(func() { - stopped = true - }) - if stopped { - a.cancel(true, Canceled, nil) - } - return stopped - } -} - -type afterFuncer interface { - AfterFunc(func()) func() bool -} - -type afterFuncCtx struct { - cancelCtx - once sync.Once // either starts running f or stops f from running - f func() -} - -func (a *afterFuncCtx) cancel(removeFromParent bool, err, cause error) { - a.cancelCtx.cancel(false, err, cause) - if removeFromParent { - removeChild(a.Context, a) - } - a.once.Do(func() { - go a.f() - }) -} - -// A stopCtx is used as the parent context of a cancelCtx when -// an AfterFunc has been registered with the parent. -// It holds the stop function used to unregister the AfterFunc. -type stopCtx struct { - Context - stop func() bool -} - -// &cancelCtxKey is the key that a cancelCtx returns itself for. -var cancelCtxKey int - -// parentCancelCtx returns the underlying *cancelCtx for parent. -// It does this by looking up parent.Value(&cancelCtxKey) to find -// the innermost enclosing *cancelCtx and then checking whether -// parent.Done() matches that *cancelCtx. (If not, the *cancelCtx -// has been wrapped in a custom implementation providing a -// different done channel, in which case we should not bypass it.) -func parentCancelCtx(parent Context) (*cancelCtx, bool) { - done := parent.Done() - if done == closedchan || done == nil { - return nil, false - } - p, ok := parent.Value(&cancelCtxKey).(*cancelCtx) - if !ok { - return nil, false - } - pdone, _ := p.done.Load().(chan struct{}) - if pdone != done { - return nil, false - } - return p, true -} - -// removeChild removes a context from its parent. -func removeChild(parent Context, child canceler) { - if s, ok := parent.(stopCtx); ok { - s.stop() - return - } - p, ok := parentCancelCtx(parent) - if !ok { - return - } - p.mu.Lock() - if p.children != nil { - delete(p.children, child) - } - p.mu.Unlock() -} - -// A canceler is a context type that can be canceled directly. The -// implementations are *cancelCtx and *timerCtx. -type canceler interface { - cancel(removeFromParent bool, err, cause error) - Done() <-chan struct{} -} - -// closedchan is a reusable closed channel. -var closedchan = make(chan struct{}) - -func init() { - close(closedchan) -} - -// A cancelCtx can be canceled. When canceled, it also cancels any children -// that implement canceler. -type cancelCtx struct { - Context - - mu sync.Mutex // protects following fields - done atomic.Value // of chan struct{}, created lazily, closed by first cancel call - children map[canceler]struct{} // set to nil by the first cancel call - err error // set to non-nil by the first cancel call - cause error // set to non-nil by the first cancel call -} - -func (c *cancelCtx) Value(key interface{}) interface{} { - if key == &cancelCtxKey { - return c - } - return value(c.Context, key) -} - -func (c *cancelCtx) Done() <-chan struct{} { - d := c.done.Load() - if d != nil { - return d.(chan struct{}) - } - c.mu.Lock() - defer c.mu.Unlock() - d = c.done.Load() - if d == nil { - d = make(chan struct{}) - c.done.Store(d) - } - return d.(chan struct{}) -} - -func (c *cancelCtx) Err() error { - c.mu.Lock() - err := c.err - c.mu.Unlock() - return err -} - -// propagateCancel arranges for child to be canceled when parent is. -// It sets the parent context of cancelCtx. -func (c *cancelCtx) propagateCancel(parent Context, child canceler) { - c.Context = parent - - done := parent.Done() - if done == nil { - return // parent is never canceled - } - - select { - case <-done: - // parent is already canceled - child.cancel(false, parent.Err(), Cause(parent)) - return - default: - } - - if p, ok := parentCancelCtx(parent); ok { - // parent is a *cancelCtx, or derives from one. - p.mu.Lock() - if p.err != nil { - // parent has already been canceled - child.cancel(false, p.err, p.cause) - } else { - if p.children == nil { - p.children = make(map[canceler]struct{}) - } - p.children[child] = struct{}{} - } - p.mu.Unlock() - return - } - - if a, ok := parent.(afterFuncer); ok { - // parent implements an AfterFunc method. - c.mu.Lock() - stop := a.AfterFunc(func() { - child.cancel(false, parent.Err(), Cause(parent)) - }) - c.Context = stopCtx{ - Context: parent, - stop: stop, - } - c.mu.Unlock() - return - } - - go func() { - select { - case <-parent.Done(): - child.cancel(false, parent.Err(), Cause(parent)) - case <-child.Done(): - } - }() -} - -type stringer interface { - String() string -} - -func contextName(c Context) string { - if s, ok := c.(stringer); ok { - return s.String() - } - return reflectlite.TypeOf(c).String() -} - -func (c *cancelCtx) String() string { - return contextName(c.Context) + ".WithCancel" -} - -// cancel closes c.done, cancels each of c's children, and, if -// removeFromParent is true, removes c from its parent's children. -// cancel sets c.cause to cause if this is the first time c is canceled. -func (c *cancelCtx) cancel(removeFromParent bool, err, cause error) { - if err == nil { - panic("context: internal error: missing cancel error") - } - if cause == nil { - cause = err - } - c.mu.Lock() - if c.err != nil { - c.mu.Unlock() - return // already canceled - } - c.err = err - c.cause = cause - d, _ := c.done.Load().(chan struct{}) - if d == nil { - c.done.Store(closedchan) - } else { - close(d) - } - for child := range c.children { - // NOTE: acquiring the child's lock while holding parent's lock. - child.cancel(false, err, cause) - } - c.children = nil - c.mu.Unlock() - - if removeFromParent { - removeChild(c.Context, c) - } -} - -// WithoutCancel returns a copy of parent that is not canceled when parent is canceled. -// The returned context returns no Deadline or Err, and its Done channel is nil. -// Calling [Cause] on the returned context returns nil. -func WithoutCancel(parent Context) Context { - if parent == nil { - panic("cannot create context from nil parent") - } - return withoutCancelCtx{parent} -} - -type withoutCancelCtx struct { - c Context -} - -func (withoutCancelCtx) Deadline() (deadline time.Time, ok bool) { - return -} - -func (withoutCancelCtx) Done() <-chan struct{} { - return nil -} - -func (withoutCancelCtx) Err() error { - return nil -} - -func (c withoutCancelCtx) Value(key interface{}) interface{} { - return value(c, key) -} - -func (c withoutCancelCtx) String() string { - return contextName(c.c) + ".WithoutCancel" -} - -// WithDeadline returns a copy of the parent context with the deadline adjusted -// to be no later than d. If the parent's deadline is already earlier than d, -// WithDeadline(parent, d) is semantically equivalent to parent. The returned -// [Context.Done] channel is closed when the deadline expires, when the returned -// cancel function is called, or when the parent context's Done channel is -// closed, whichever happens first. -// -// Canceling this context releases resources associated with it, so code should -// call cancel as soon as the operations running in this [Context] complete. -func WithDeadline(parent Context, d time.Time) (Context, CancelFunc) { - return WithDeadlineCause(parent, d, nil) -} - -// WithDeadlineCause behaves like [WithDeadline] but also sets the cause of the -// returned Context when the deadline is exceeded. The returned [CancelFunc] does -// not set the cause. -func WithDeadlineCause(parent Context, d time.Time, cause error) (Context, CancelFunc) { - if parent == nil { - panic("cannot create context from nil parent") - } - if cur, ok := parent.Deadline(); ok && cur.Before(d) { - // The current deadline is already sooner than the new one. - return WithCancel(parent) - } - c := &timerCtx{ - deadline: d, - } - c.cancelCtx.propagateCancel(parent, c) - dur := time.Until(d) - if dur <= 0 { - c.cancel(true, DeadlineExceeded, cause) // deadline has already passed - return c, func() { c.cancel(false, Canceled, nil) } - } - c.mu.Lock() - defer c.mu.Unlock() - if c.err == nil { - c.timer = time.AfterFunc(dur, func() { - c.cancel(true, DeadlineExceeded, cause) - }) - } - return c, func() { c.cancel(true, Canceled, nil) } -} - -// A timerCtx carries a timer and a deadline. It embeds a cancelCtx to -// implement Done and Err. It implements cancel by stopping its timer then -// delegating to cancelCtx.cancel. -type timerCtx struct { - cancelCtx - timer *time.Timer // Under cancelCtx.mu. - - deadline time.Time -} - -func (c *timerCtx) Deadline() (deadline time.Time, ok bool) { - return c.deadline, true -} - -func (c *timerCtx) String() string { - return contextName(c.cancelCtx.Context) + ".WithDeadline(" + - c.deadline.String() + " [" + - time.Until(c.deadline).String() + "])" -} - -func (c *timerCtx) cancel(removeFromParent bool, err, cause error) { - c.cancelCtx.cancel(false, err, cause) - if removeFromParent { - // Remove this timerCtx from its parent cancelCtx's children. - removeChild(c.cancelCtx.Context, c) - } - c.mu.Lock() - if c.timer != nil { - c.timer.Stop() - c.timer = nil - } - c.mu.Unlock() -} - -// WithTimeout returns WithDeadline(parent, time.Now().Add(timeout)). -// -// Canceling this context releases resources associated with it, so code should -// call cancel as soon as the operations running in this [Context] complete: -// -// func slowOperationWithTimeout(ctx context.Context) (Result, error) { -// ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond) -// defer cancel() // releases resources if slowOperation completes before timeout elapses -// return slowOperation(ctx) -// } -func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) { - return WithDeadline(parent, time.Now().Add(timeout)) -} - -// WithTimeoutCause behaves like [WithTimeout] but also sets the cause of the -// returned Context when the timeout expires. The returned [CancelFunc] does -// not set the cause. -func WithTimeoutCause(parent Context, timeout time.Duration, cause error) (Context, CancelFunc) { - return WithDeadlineCause(parent, time.Now().Add(timeout), cause) -} - -// WithValue returns a copy of parent in which the value associated with key is -// val. -// -// Use context Values only for request-scoped data that transits processes and -// APIs, not for passing optional parameters to functions. -// -// The provided key must be comparable and should not be of type -// string or any other built-in type to avoid collisions between -// packages using context. Users of WithValue should define their own -// types for keys. To avoid allocating when assigning to an -// interface{}, context keys often have concrete type -// struct{}. Alternatively, exported context key variables' static -// type should be a pointer or interface. -func WithValue(parent Context, key, val interface{}) Context { - if parent == nil { - panic("cannot create context from nil parent") - } - if key == nil { - panic("nil key") - } - if !reflectlite.TypeOf(key).Comparable() { - panic("key is not comparable") - } - return &valueCtx{parent, key, val} -} - -// A valueCtx carries a key-value pair. It implements Value for that key and -// delegates all other calls to the embedded Context. -type valueCtx struct { - Context - key, val interface{} -} - -// stringify tries a bit to stringify v, without using fmt, since we don't -// want context depending on the unicode tables. This is only used by -// *valueCtx.String(). -func stringify(v interface{}) string { - switch s := v.(type) { - case stringer: - return s.String() - case string: - return s - } - return "<not Stringer>" -} - -func (c *valueCtx) String() string { - return contextName(c.Context) + ".WithValue(type " + - reflectlite.TypeOf(c.key).String() + - ", val " + stringify(c.val) + ")" -} - -func (c *valueCtx) Value(key interface{}) interface{} { - if c.key == key { - return c.val - } - return value(c.Context, key) -} - -func value(c Context, key interface{}) interface{} { - for { - switch ctx := c.(type) { - case *valueCtx: - if key == ctx.key { - return ctx.val - } - c = ctx.Context - case *cancelCtx: - if key == &cancelCtxKey { - return c - } - c = ctx.Context - case withoutCancelCtx: - if key == &cancelCtxKey { - // This implements Cause(ctx) == nil - // when ctx is created using WithoutCancel. - return nil - } - c = ctx.c - case *timerCtx: - if key == &cancelCtxKey { - return &ctx.cancelCtx - } - c = ctx.Context - case backgroundCtx, todoCtx: - return nil - default: - return c.Value(key) - } - } -} diff --git a/internal/ctxt/merge.go b/internal/ctxt/merge.go deleted file mode 100644 index 7604c78e6e..0000000000 --- a/internal/ctxt/merge.go +++ /dev/null @@ -1,52 +0,0 @@ -// package ctxt implements context merging. -package ctxt - -import ( - "context" - "time" -) - -type mergeContext struct { - context.Context - ctx2 context.Context -} - -// Merge returns a context that is cancelled when at least one of the parents -// is cancelled. The returned context also returns the values of ctx1, or ctx2 -// if nil. -func Merge(ctx1, ctx2 context.Context) (context.Context, context.CancelFunc) { - ctx, cancel := WithCancelCause(ctx1) - stop := AfterFunc(ctx2, func() { - cancel(Cause(ctx2)) - }) - - return &mergeContext{ - Context: ctx, - ctx2: ctx2, - }, func() { - stop() - cancel(context.Canceled) - } -} - -// Value returns ctx2's value if ctx's is nil. -func (ctx *mergeContext) Value(key interface{}) interface{} { - if v := ctx.Context.Value(key); v != nil { - return v - } - return ctx.ctx2.Value(key) -} - -// Deadline returns the earlier deadline of the two parents of ctx. -func (ctx *mergeContext) Deadline() (time.Time, bool) { - if d1, ok := ctx.Context.Deadline(); ok { - if d2, ok := ctx.ctx2.Deadline(); ok { - if d1.Before(d2) { - return d1, true - } - return d2, true - } - return d1, ok - } - return ctx.ctx2.Deadline() -} diff --git a/internal/ctxt/merge_test.go b/internal/ctxt/merge_test.go deleted file mode 100644 index c111ae7a09..0000000000 --- a/internal/ctxt/merge_test.go +++ /dev/null @@ -1,133 +0,0 @@ -package ctxt_test - -import ( - "context" - "testing" - "time" - - "github.com/gophercloud/gophercloud/internal/ctxt" -) - -func TestMerge(t *testing.T) { - t.Run("returns values from both parents", func(t *testing.T) { - ctx1 := context.WithValue(context.Background(), - "key1", "value1") - - ctx2 := context.WithValue(context.WithValue(context.Background(), - "key1", "this value should be overridden"), - "key2", "value2") - - ctx, cancel := ctxt.Merge(ctx1, ctx2) - defer cancel() - - if v1 := ctx.Value("key1"); v1 != nil { - if s1, ok := v1.(string); ok { - if s1 != "value1" { - t.Errorf("found value for key1 %q, expected %q", s1, "value1") - } - } else { - t.Errorf("key1 is not the expected type string") - } - } else { - t.Errorf("key1 returned nil") - } - - if v2 := ctx.Value("key2"); v2 != nil { - if s2, ok := v2.(string); ok { - if s2 != "value2" { - t.Errorf("found value for key2 %q, expected %q", s2, "value2") - } - } else { - t.Errorf("key2 is not the expected type string") - } - } else { - t.Errorf("key2 returned nil") - } - }) - - t.Run("first parent cancels", func(t *testing.T) { - ctx1, cancel1 := context.WithCancel(context.Background()) - ctx2, cancel2 := context.WithCancel(context.Background()) - defer cancel2() - - ctx, cancel := ctxt.Merge(ctx1, ctx2) - defer cancel() - - if err := ctx.Err(); err != nil { - t.Errorf("context unexpectedly done: %v", err) - } - - cancel1() - time.Sleep(1 * time.Millisecond) - if err := ctx.Err(); err == nil { - t.Errorf("context not done despite parent1 cancelled") - } - }) - - t.Run("second parent cancels", func(t *testing.T) { - ctx1, cancel1 := context.WithCancel(context.Background()) - ctx2, cancel2 := context.WithCancel(context.Background()) - defer cancel1() - - ctx, cancel := ctxt.Merge(ctx1, ctx2) - defer cancel() - - if err := ctx.Err(); err != nil { - t.Errorf("context unexpectedly done: %v", err) - } - - cancel2() - time.Sleep(1 * time.Millisecond) - if err := ctx.Err(); err == nil { - t.Errorf("context not done despite parent2 cancelled") - } - }) - - t.Run("inherits deadline from first parent", func(t *testing.T) { - now := time.Now() - t1 := now.Add(time.Hour) - t2 := t1.Add(time.Second) - - ctx1, cancel1 := context.WithDeadline(context.Background(), t1) - ctx2, cancel2 := context.WithDeadline(context.Background(), t2) - defer cancel1() - defer cancel2() - - ctx, cancel := ctxt.Merge(ctx1, ctx2) - defer cancel() - - if err := ctx.Err(); err != nil { - t.Errorf("context unexpectedly done: %v", err) - } - - if deadline, ok := ctx.Deadline(); ok { - if deadline != t1 { - t.Errorf("expected deadline to be %v, found %v", t1, deadline) - } - } - }) - - t.Run("inherits deadline from second parent", func(t *testing.T) { - now := time.Now() - t2 := now.Add(time.Hour) - t1 := t2.Add(time.Second) - - ctx1, cancel1 := context.WithDeadline(context.Background(), t1) - ctx2, cancel2 := context.WithDeadline(context.Background(), t2) - defer cancel1() - defer cancel2() - - ctx, cancel := ctxt.Merge(ctx1, ctx2) - defer cancel() - - if err := ctx.Err(); err != nil { - t.Errorf("context unexpectedly done: %v", err) - } - - if deadline, ok := ctx.Deadline(); ok { - if deadline != t2 { - t.Errorf("expected deadline to be %v, found %v", t2, deadline) - } - } - }) -} diff --git a/provider_client.go b/provider_client.go index 64787e5337..add4744ec5 100644 --- a/provider_client.go +++ b/provider_client.go @@ -10,8 +10,6 @@ import ( "net/http" "strings" "sync" - - "github.com/gophercloud/gophercloud/internal/ctxt" ) // DefaultUserAgent is the default User-Agent string set in the request header. @@ -90,9 +88,7 @@ type ProviderClient struct { // with the token and reauth func zeroed. Such client can be used to perform reauthorization. Throwaway bool - // Context is the context passed to the HTTP request. Values set on the - // per-call context, when available, override values set on this - // context. + // Context is the context passed to the HTTP request. Context context.Context // Retry backoff func is called when rate limited. @@ -356,20 +352,15 @@ type requestState struct { var applicationJSON = "application/json" -// RequestWithContext performs an HTTP request using the ProviderClient's -// current HTTPClient. An authentication header will automatically be provided. -func (client *ProviderClient) RequestWithContext(ctx context.Context, method, url string, options *RequestOpts) (*http.Response, error) { - return client.doRequest(ctx, method, url, options, &requestState{ +// Request performs an HTTP request using the ProviderClient's current HTTPClient. An authentication +// header will automatically be provided. +func (client *ProviderClient) Request(method, url string, options *RequestOpts) (*http.Response, error) { + return client.doRequest(method, url, options, &requestState{ hasReauthenticated: false, }) } -// Request is a compatibility wrapper for Request. -func (client *ProviderClient) Request(method, url string, options *RequestOpts) (*http.Response, error) { - return client.RequestWithContext(context.Background(), method, url, options) -} - -func (client *ProviderClient) doRequest(ctx context.Context, method, url string, options *RequestOpts, state *requestState) (*http.Response, error) { +func (client *ProviderClient) doRequest(method, url string, options *RequestOpts, state *requestState) (*http.Response, error) { var body io.Reader var contentType *string @@ -398,16 +389,14 @@ func (client *ProviderClient) doRequest(ctx context.Context, method, url string, body = options.RawBody } - if client.Context != nil { - var cancel context.CancelFunc - ctx, cancel = ctxt.Merge(ctx, client.Context) - defer cancel() - } - - req, err := http.NewRequestWithContext(ctx, method, url, body) + // Construct the http.Request. + req, err := http.NewRequest(method, url, body) if err != nil { return nil, err } + if client.Context != nil { + req = req.WithContext(client.Context) + } // Populate the request headers. // Apply options.MoreHeaders and options.OmitHeaders, to give the caller the chance to @@ -443,12 +432,12 @@ func (client *ProviderClient) doRequest(ctx context.Context, method, url string, if client.RetryFunc != nil { var e error state.retries = state.retries + 1 - e = client.RetryFunc(ctx, method, url, options, err, state.retries) + e = client.RetryFunc(client.Context, method, url, options, err, state.retries) if e != nil { return nil, e } - return client.doRequest(ctx, method, url, options, state) + return client.doRequest(method, url, options, state) } return nil, err } @@ -502,7 +491,7 @@ func (client *ProviderClient) doRequest(ctx context.Context, method, url string, } } state.hasReauthenticated = true - resp, err = client.doRequest(ctx, method, url, options, state) + resp, err = client.doRequest(method, url, options, state) if err != nil { switch err.(type) { case *ErrUnexpectedResponseCode: @@ -567,7 +556,7 @@ func (client *ProviderClient) doRequest(ctx context.Context, method, url string, return resp, e } - return client.doRequest(ctx, method, url, options, state) + return client.doRequest(method, url, options, state) } case http.StatusInternalServerError: err = ErrDefault500{respErr} @@ -603,7 +592,7 @@ func (client *ProviderClient) doRequest(ctx context.Context, method, url string, return resp, e } - return client.doRequest(ctx, method, url, options, state) + return client.doRequest(method, url, options, state) } return resp, err @@ -627,7 +616,7 @@ func (client *ProviderClient) doRequest(ctx context.Context, method, url string, return resp, e } - return client.doRequest(ctx, method, url, options, state) + return client.doRequest(method, url, options, state) } return nil, err } diff --git a/service_client.go b/service_client.go index b8e6fd1a38..94a161e340 100644 --- a/service_client.go +++ b/service_client.go @@ -1,7 +1,6 @@ package gophercloud import ( - "context" "io" "net/http" "strings" @@ -60,88 +59,58 @@ func (client *ServiceClient) initReqOpts(JSONBody interface{}, JSONResponse inte } } -// GetWithContext calls `Request` with the "GET" HTTP verb. -func (client *ServiceClient) GetWithContext(ctx context.Context, url string, JSONResponse interface{}, opts *RequestOpts) (*http.Response, error) { - if opts == nil { - opts = new(RequestOpts) - } - client.initReqOpts(nil, JSONResponse, opts) - return client.RequestWithContext(ctx, "GET", url, opts) -} - -// Get is a compatibility wrapper for GetWithContext. +// Get calls `Request` with the "GET" HTTP verb. func (client *ServiceClient) Get(url string, JSONResponse interface{}, opts *RequestOpts) (*http.Response, error) { - return client.GetWithContext(context.Background(), url, JSONResponse, opts) -} - -// PostWithContext calls `Request` with the "POST" HTTP verb. -func (client *ServiceClient) PostWithContext(ctx context.Context, url string, JSONBody interface{}, JSONResponse interface{}, opts *RequestOpts) (*http.Response, error) { if opts == nil { opts = new(RequestOpts) } - client.initReqOpts(JSONBody, JSONResponse, opts) - return client.RequestWithContext(ctx, "POST", url, opts) + client.initReqOpts(nil, JSONResponse, opts) + return client.Request("GET", url, opts) } -// Post is a compatibility wrapper for PostWithContext. +// Post calls `Request` with the "POST" HTTP verb. func (client *ServiceClient) Post(url string, JSONBody interface{}, JSONResponse interface{}, opts *RequestOpts) (*http.Response, error) { - return client.PostWithContext(context.Background(), url, JSONBody, JSONResponse, opts) -} - -// PutWithContext calls `Request` with the "PUT" HTTP verb. -func (client *ServiceClient) PutWithContext(ctx context.Context, url string, JSONBody interface{}, JSONResponse interface{}, opts *RequestOpts) (*http.Response, error) { if opts == nil { opts = new(RequestOpts) } client.initReqOpts(JSONBody, JSONResponse, opts) - return client.RequestWithContext(ctx, "PUT", url, opts) + return client.Request("POST", url, opts) } -// Put is a compatibility wrapper for PurWithContext. +// Put calls `Request` with the "PUT" HTTP verb. func (client *ServiceClient) Put(url string, JSONBody interface{}, JSONResponse interface{}, opts *RequestOpts) (*http.Response, error) { - return client.PutWithContext(context.Background(), url, JSONBody, JSONResponse, opts) -} - -// PatchWithContext calls `Request` with the "PATCH" HTTP verb. -func (client *ServiceClient) PatchWithContext(ctx context.Context, url string, JSONBody interface{}, JSONResponse interface{}, opts *RequestOpts) (*http.Response, error) { if opts == nil { opts = new(RequestOpts) } client.initReqOpts(JSONBody, JSONResponse, opts) - return client.RequestWithContext(ctx, "PATCH", url, opts) + return client.Request("PUT", url, opts) } -// Patch is a compatibility wrapper for PatchWithContext. +// Patch calls `Request` with the "PATCH" HTTP verb. func (client *ServiceClient) Patch(url string, JSONBody interface{}, JSONResponse interface{}, opts *RequestOpts) (*http.Response, error) { - return client.PatchWithContext(context.Background(), url, JSONBody, JSONResponse, opts) -} - -// DeleteWithContext calls `Request` with the "DELETE" HTTP verb. -func (client *ServiceClient) DeleteWithContext(ctx context.Context, url string, opts *RequestOpts) (*http.Response, error) { if opts == nil { opts = new(RequestOpts) } - client.initReqOpts(nil, nil, opts) - return client.RequestWithContext(ctx, "DELETE", url, opts) + client.initReqOpts(JSONBody, JSONResponse, opts) + return client.Request("PATCH", url, opts) } -// Delete is a compatibility wrapper for DeleteWithContext. +// Delete calls `Request` with the "DELETE" HTTP verb. func (client *ServiceClient) Delete(url string, opts *RequestOpts) (*http.Response, error) { - return client.DeleteWithContext(context.Background(), url, opts) -} - -// HeadWithContext calls `Request` with the "HEAD" HTTP verb. -func (client *ServiceClient) HeadWithContext(ctx context.Context, url string, opts *RequestOpts) (*http.Response, error) { if opts == nil { opts = new(RequestOpts) } client.initReqOpts(nil, nil, opts) - return client.RequestWithContext(ctx, "HEAD", url, opts) + return client.Request("DELETE", url, opts) } -// Head is a compatibility wrapper for HeadWithContext. +// Head calls `Request` with the "HEAD" HTTP verb. func (client *ServiceClient) Head(url string, opts *RequestOpts) (*http.Response, error) { - return client.HeadWithContext(context.Background(), url, opts) + if opts == nil { + opts = new(RequestOpts) + } + client.initReqOpts(nil, nil, opts) + return client.Request("HEAD", url, opts) } func (client *ServiceClient) setMicroversionHeader(opts *RequestOpts) { @@ -164,7 +133,7 @@ func (client *ServiceClient) setMicroversionHeader(opts *RequestOpts) { } // Request carries out the HTTP operation for the service client -func (client *ServiceClient) RequestWithContext(ctx context.Context, method, url string, options *RequestOpts) (*http.Response, error) { +func (client *ServiceClient) Request(method, url string, options *RequestOpts) (*http.Response, error) { if options.MoreHeaders == nil { options.MoreHeaders = make(map[string]string) } @@ -182,12 +151,7 @@ func (client *ServiceClient) RequestWithContext(ctx context.Context, method, url options.MoreHeaders[k] = v } } - return client.ProviderClient.RequestWithContext(ctx, method, url, options) -} - -// Request is a compatibility wrapper for RequestWithContext. -func (client *ServiceClient) Request(method, url string, options *RequestOpts) (*http.Response, error) { - return client.RequestWithContext(context.Background(), method, url, options) + return client.ProviderClient.Request(method, url, options) } // ParseResponse is a helper function to parse http.Response to constituents. From f7678f869b1431dd5e0d96323a79c445bcf3da78 Mon Sep 17 00:00:00 2001 From: Pierre Prinetti <pierreprinetti@redhat.com> Date: Wed, 6 Mar 2024 17:03:52 +0100 Subject: [PATCH 348/360] Prepare v1.11.0 When the Provider client contains a non-nil Context, the [`doRequest` method](https://github.com/gophercloud/gophercloud/blob/5b44c45ec947301d531178302c4cecc0a923ad96/provider_client.go#L401-L405) of the Provider client generated a new context by merging the Provider client's and the per-call context. However the new context is too short-lived to let the callers consume a response body. This version reverts the inclusion of Context in the v1 branch. This version replaces v1.9 and v1.10. --- CHANGELOG.md | 10 ++++++++-- provider_client.go | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3d99d01ff2..0eec3e6dd6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,10 @@ -## v1.10.0 (2024-02-27) +## 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 @@ -8,7 +14,7 @@ * [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) +## v1.9.0 (2024-02-02) **RETRACTED**: see https://github.com/gophercloud/gophercloud/issues/2969 New features and improvements: diff --git a/provider_client.go b/provider_client.go index add4744ec5..1ff54b8197 100644 --- a/provider_client.go +++ b/provider_client.go @@ -14,7 +14,7 @@ import ( // DefaultUserAgent is the default User-Agent string set in the request header. const ( - DefaultUserAgent = "gophercloud/v1.10.0" + DefaultUserAgent = "gophercloud/v1.11.0" DefaultMaxBackoffRetries = 60 ) From ab6cdcfd7ed65b14b840765787cf3265268d8071 Mon Sep 17 00:00:00 2001 From: Ludovic Lamarche <ludovic.lamarche@corp.ovh.com> Date: Wed, 28 Jul 2021 15:35:40 +0000 Subject: [PATCH 349/360] update live migrate api with microversion 2.25 --- .../v2/extensions/migrate/microversions.go | 25 +++++++++++++++++++ .../migrate/testing/fixtures_test.go | 15 +++++++++++ .../migrate/testing/requests_test.go | 19 ++++++++++++++ 3 files changed, 59 insertions(+) create mode 100644 openstack/compute/v2/extensions/migrate/microversions.go diff --git a/openstack/compute/v2/extensions/migrate/microversions.go b/openstack/compute/v2/extensions/migrate/microversions.go new file mode 100644 index 0000000000..7ee8f0e058 --- /dev/null +++ b/openstack/compute/v2/extensions/migrate/microversions.go @@ -0,0 +1,25 @@ +package migrate + +import "github.com/gophercloud/gophercloud" + +// LiveMigrateOpts specifies parameters of live migrate action. +type LiveMigrate225Opts 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 string `json:"block_migration"` + + // 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 LiveMigrate225Opts) ToLiveMigrateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "os-migrateLive") +} diff --git a/openstack/compute/v2/extensions/migrate/testing/fixtures_test.go b/openstack/compute/v2/extensions/migrate/testing/fixtures_test.go index 1d2f5902c2..9deb1ff562 100644 --- a/openstack/compute/v2/extensions/migrate/testing/fixtures_test.go +++ b/openstack/compute/v2/extensions/migrate/testing/fixtures_test.go @@ -31,3 +31,18 @@ func mockLiveMigrateResponse(t *testing.T, id string) { w.WriteHeader(http.StatusAccepted) }) } + +func mockLiveMigrate225Response(t *testing.T, id string) { + th.Mux.HandleFunc("/servers/"+id+"/action", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + th.TestJSONRequest(t, r, `{ + "os-migrateLive": { + "host": "01c0cadef72d47e28a672a76060d492c", + "block_migration": "auto", + "disk_over_commit": true + } + }`) + w.WriteHeader(http.StatusAccepted) + }) +} diff --git a/openstack/compute/v2/extensions/migrate/testing/requests_test.go b/openstack/compute/v2/extensions/migrate/testing/requests_test.go index b6906b7839..68968be432 100644 --- a/openstack/compute/v2/extensions/migrate/testing/requests_test.go +++ b/openstack/compute/v2/extensions/migrate/testing/requests_test.go @@ -39,3 +39,22 @@ func TestLiveMigrate(t *testing.T) { err := migrate.LiveMigrate(client.ServiceClient(), serverID, migrationOpts).ExtractErr() th.AssertNoErr(t, err) } + +func TestLiveMigrate225(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + mockLiveMigrate225Response(t, serverID) + + host := "01c0cadef72d47e28a672a76060d492c" + diskOverCommit := true + + migrationOpts := migrate.LiveMigrate225Opts{ + Host: &host, + BlockMigration: "auto", + DiskOverCommit: &diskOverCommit, + } + + err := migrate.LiveMigrate(client.ServiceClient(), serverID, migrationOpts).ExtractErr() + th.AssertNoErr(t, err) +} From bc3fd0b887a440beb2261728eb11ec16aab92dc9 Mon Sep 17 00:00:00 2001 From: Benjamin Maisonnas <benjamin.maisonnas@ovhcloud.com> Date: Thu, 18 Nov 2021 14:33:42 +0000 Subject: [PATCH 350/360] octavia: add DomainName and HTTPVersion on monitors (added in 2.10) --- .../openstack/loadbalancer/v2/loadbalancer.go | 9 +++ .../v2/loadbalancers/testing/fixtures_test.go | 6 ++ .../v2/loadbalancers/testing/requests_test.go | 2 + openstack/loadbalancer/v2/monitors/doc.go | 2 + .../loadbalancer/v2/monitors/requests.go | 58 ++++++++++++------- openstack/loadbalancer/v2/monitors/results.go | 8 +++ .../v2/monitors/testing/fixtures_test.go | 14 +++++ .../v2/monitors/testing/requests_test.go | 4 ++ 8 files changed, 83 insertions(+), 20 deletions(-) diff --git a/internal/acceptance/openstack/loadbalancer/v2/loadbalancer.go b/internal/acceptance/openstack/loadbalancer/v2/loadbalancer.go index b5eda52e18..2fb82aa887 100644 --- a/internal/acceptance/openstack/loadbalancer/v2/loadbalancer.go +++ b/internal/acceptance/openstack/loadbalancer/v2/loadbalancer.go @@ -178,6 +178,7 @@ func CreateLoadBalancerFullyPopulated(t *testing.T, client *gophercloud.ServiceC memberName := tools.RandomString("TESTACCT-", 8) memberPort := tools.RandomInt(100, 1000) memberWeight := tools.RandomInt(1, 10) + monitorDomainName := tools.RandomString("example.com", 8) t.Logf("Attempting to create fully populated loadbalancer %s on subnet %s which contains listener: %s, l7Policy: %s, pool %s, member %s", lbName, subnetID, listenerName, policyName, poolName, memberName) @@ -206,9 +207,11 @@ func CreateLoadBalancerFullyPopulated(t *testing.T, client *gophercloud.ServiceC }}, Monitor: &monitors.CreateOpts{ Delay: 10, + DomainName: monitorDomainName, Timeout: 5, MaxRetries: 5, MaxRetriesDown: 4, + HTTPVersion: 1.1, Type: monitors.TypeHTTP, }, }, @@ -268,6 +271,8 @@ func CreateLoadBalancerFullyPopulated(t *testing.T, client *gophercloud.ServiceC th.AssertEquals(t, lb.Pools[0].Members[0].ProtocolPort, memberPort) th.AssertEquals(t, lb.Pools[0].Members[0].Weight, memberWeight) + th.AssertEquals(t, lb.Pools[0].Monitor.DomainName, monitorDomainName) + if len(tags) > 0 { th.AssertDeepEquals(t, lb.Tags, tags) } @@ -325,9 +330,11 @@ func CreateMonitor(t *testing.T, client *gophercloud.ServiceClient, lb *loadbala PoolID: pool.ID, Name: monitorName, Delay: 10, + DomainName: "example.com", Timeout: 5, MaxRetries: 5, MaxRetriesDown: 4, + HTTPVersion: 1.1, Type: monitors.TypePING, } @@ -345,9 +352,11 @@ func CreateMonitor(t *testing.T, client *gophercloud.ServiceClient, lb *loadbala th.AssertEquals(t, monitor.Name, monitorName) th.AssertEquals(t, monitor.Type, monitors.TypePING) th.AssertEquals(t, monitor.Delay, 10) + th.AssertEquals(t, monitor.DomainName, "example.com") th.AssertEquals(t, monitor.Timeout, 5) th.AssertEquals(t, monitor.MaxRetries, 5) th.AssertEquals(t, monitor.MaxRetriesDown, 4) + th.AssertEquals(t, monitor.HTTPVersion, 1.1) return monitor, nil } diff --git a/openstack/loadbalancer/v2/loadbalancers/testing/fixtures_test.go b/openstack/loadbalancer/v2/loadbalancers/testing/fixtures_test.go index e0ddcb72e0..5a8ce9e362 100644 --- a/openstack/loadbalancer/v2/loadbalancers/testing/fixtures_test.go +++ b/openstack/loadbalancer/v2/loadbalancers/testing/fixtures_test.go @@ -159,9 +159,11 @@ const PostFullyPopulatedLoadbalancerBody = ` "admin_state_up": true, "project_id": "e3cd678b11784734bc366148aa37580e", "delay": 3, + "domain_name": "example.com", "expected_codes": "200,201,202", "max_retries": 2, "http_method": "GET", + "http_version": 1.1, "timeout": 1, "max_retries_down": 3, "url_path": "/index.html", @@ -382,8 +384,10 @@ var ( Timeout: 1, MaxRetries: 2, Delay: 3, + DomainName: "example.com", MaxRetriesDown: 3, HTTPMethod: "GET", + HTTPVersion: 1.1, URLPath: "/index.html", ExpectedCodes: "200,201,202", AdminStateUp: true, @@ -465,6 +469,7 @@ func HandleFullyPopulatedLoadbalancerCreationSuccessfully(t *testing.T, response "default_pool": { "healthmonitor": { "delay": 3, + "domain_name": "example.com", "expected_codes": "200", "http_method": "GET", "max_retries": 2, @@ -472,6 +477,7 @@ func HandleFullyPopulatedLoadbalancerCreationSuccessfully(t *testing.T, response "name": "db", "timeout": 1, "type": "HTTP", + "http_version": 1.1, "url_path": "/index.html" }, "lb_algorithm": "ROUND_ROBIN", diff --git a/openstack/loadbalancer/v2/loadbalancers/testing/requests_test.go b/openstack/loadbalancer/v2/loadbalancers/testing/requests_test.go index fbd33c648c..8a420621bb 100644 --- a/openstack/loadbalancer/v2/loadbalancers/testing/requests_test.go +++ b/openstack/loadbalancer/v2/loadbalancers/testing/requests_test.go @@ -121,9 +121,11 @@ func TestCreateFullyPopulatedLoadbalancer(t *testing.T) { Name: "db", Type: "HTTP", Delay: 3, + DomainName: "example.com", Timeout: 1, MaxRetries: 2, MaxRetriesDown: 3, + HTTPVersion: 1.1, URLPath: "/index.html", HTTPMethod: "GET", ExpectedCodes: "200", diff --git a/openstack/loadbalancer/v2/monitors/doc.go b/openstack/loadbalancer/v2/monitors/doc.go index b191b45e9c..c6774594e5 100644 --- a/openstack/loadbalancer/v2/monitors/doc.go +++ b/openstack/loadbalancer/v2/monitors/doc.go @@ -29,6 +29,7 @@ Example to Create a Monitor Name: "db", PoolID: "84f1b61f-58c4-45bf-a8a9-2dafb9e5214d", Delay: 20, + DomainName: "example.com", Timeout: 10, MaxRetries: 5, MaxRetriesDown: 4, @@ -48,6 +49,7 @@ Example to Update a Monitor updateOpts := monitors.UpdateOpts{ Name: "NewHealthmonitorName", Delay: 3, + DomainName: "www.example.com", Timeout: 20, MaxRetries: 10, MaxRetriesDown: 8, diff --git a/openstack/loadbalancer/v2/monitors/requests.go b/openstack/loadbalancer/v2/monitors/requests.go index 7d7466a081..676463cd47 100644 --- a/openstack/loadbalancer/v2/monitors/requests.go +++ b/openstack/loadbalancer/v2/monitors/requests.go @@ -19,25 +19,27 @@ type ListOptsBuilder interface { // sort by a particular Monitor attribute. SortDir sets the direction, and is // either `asc' or `desc'. Marker and Limit are used for pagination. type ListOpts struct { - ID string `q:"id"` - Name string `q:"name"` - TenantID string `q:"tenant_id"` - ProjectID string `q:"project_id"` - PoolID string `q:"pool_id"` - Type string `q:"type"` - Delay int `q:"delay"` - Timeout int `q:"timeout"` - MaxRetries int `q:"max_retries"` - MaxRetriesDown int `q:"max_retries_down"` - HTTPMethod string `q:"http_method"` - URLPath string `q:"url_path"` - ExpectedCodes string `q:"expected_codes"` - AdminStateUp *bool `q:"admin_state_up"` - Status string `q:"status"` - Limit int `q:"limit"` - Marker string `q:"marker"` - SortKey string `q:"sort_key"` - SortDir string `q:"sort_dir"` + ID string `q:"id"` + Name string `q:"name"` + TenantID string `q:"tenant_id"` + ProjectID string `q:"project_id"` + PoolID string `q:"pool_id"` + Type string `q:"type"` + Delay int `q:"delay"` + DomainName string `q:"domain_name"` + Timeout int `q:"timeout"` + MaxRetries int `q:"max_retries"` + MaxRetriesDown int `q:"max_retries_down"` + HTTPMethod string `q:"http_method"` + HTTPVersion float32 `q:"http_version"` + URLPath string `q:"url_path"` + ExpectedCodes string `q:"expected_codes"` + AdminStateUp *bool `q:"admin_state_up"` + Status string `q:"status"` + Limit int `q:"limit"` + Marker string `q:"marker"` + SortKey string `q:"sort_key"` + SortDir string `q:"sort_dir"` } // ToMonitorListQuery formats a ListOpts into a query string. @@ -103,6 +105,10 @@ type CreateOpts struct { // The time, in seconds, between sending probes to members. Delay int `json:"delay" required:"true"` + // The domain name, which be injected into the HTTP Host Header to + // the backend server for HTTP health check. New in version 2.10. + DomainName string `json:"domain_name"` + // Maximum number of seconds for a Monitor to wait for a ping reply // before it times out. The value must be less than the delay value. Timeout int `json:"timeout" required:"true"` @@ -111,7 +117,7 @@ type CreateOpts struct { // status to INACTIVE. Must be a number between 1 and 10. MaxRetries int `json:"max_retries" required:"true"` - // Number of permissible ping failures befor changing the member's + // Number of permissible ping failures before changing the member's // status to ERROR. Must be a number between 1 and 10. MaxRetriesDown int `json:"max_retries_down,omitempty"` @@ -122,6 +128,10 @@ type CreateOpts struct { // is not specified, it defaults to "GET". Required for HTTP(S) types. HTTPMethod string `json:"http_method,omitempty"` + // The HTTP version used for requests by the Monitor. If this attribute + // is not specified, it defaults to 1.0". Required for HTTP(S) types. + HTTPVersion float32 `json:"http_version,omitempty"` + // Expected HTTP codes for a passing HTTP(S) Monitor. You can either specify // a single status like "200", a range like "200-202", or a combination like // "200-202, 401". @@ -193,6 +203,10 @@ type UpdateOpts struct { // The time, in seconds, between sending probes to members. Delay int `json:"delay,omitempty"` + // The domain name, which be injected into the HTTP Host Header to + // the backend server for HTTP health check. New in version 2.10. + DomainName string `json:"domain_name"` + // Maximum number of seconds for a Monitor to wait for a ping reply // before it times out. The value must be less than the delay value. Timeout int `json:"timeout,omitempty"` @@ -213,6 +227,10 @@ type UpdateOpts struct { // is not specified, it defaults to "GET". Required for HTTP(S) types. HTTPMethod string `json:"http_method,omitempty"` + // The HTTP version used for requests by the Monitor. If this attribute + // is not specified, it defaults to 1.0". Required for HTTP(S) types. + HTTPVersion float32 `json:"http_version,omitempty"` + // Expected HTTP codes for a passing HTTP(S) Monitor. You can either specify // a single status like "200", or a range like "200-202". Required for HTTP(S) // types. diff --git a/openstack/loadbalancer/v2/monitors/results.go b/openstack/loadbalancer/v2/monitors/results.go index 502581feba..0988962e3d 100644 --- a/openstack/loadbalancer/v2/monitors/results.go +++ b/openstack/loadbalancer/v2/monitors/results.go @@ -44,6 +44,10 @@ type Monitor struct { // The time, in seconds, between sending probes to members. Delay int `json:"delay"` + // The domain name, which be injected into the HTTP Host Header to + // the backend server for HTTP health check. New in version 2.10. + DomainName string `json:"domain_name"` + // The maximum number of seconds for a monitor to wait for a connection to be // established before it times out. This value must be less than the delay // value. @@ -60,6 +64,10 @@ type Monitor struct { // The HTTP method that the monitor uses for requests. HTTPMethod string `json:"http_method"` + // The HTTP version. One of 1.0 or 1.1. The default is 1.0. + // New in version 2.10 + HTTPVersion float32 `json:"http_version"` + // The HTTP path of the request sent by the monitor to test the health of a // member. Must be a string beginning with a forward slash (/). URLPath string `json:"url_path" ` diff --git a/openstack/loadbalancer/v2/monitors/testing/fixtures_test.go b/openstack/loadbalancer/v2/monitors/testing/fixtures_test.go index c6ae88494d..2516bc5be1 100644 --- a/openstack/loadbalancer/v2/monitors/testing/fixtures_test.go +++ b/openstack/loadbalancer/v2/monitors/testing/fixtures_test.go @@ -30,11 +30,13 @@ const HealthmonitorsListBody = ` "admin_state_up":true, "project_id":"83657cfcdfe44cd5920adaf26c48ceea", "delay":5, + "domain_name": "example.com", "name":"db", "expected_codes":"200", "max_retries":2, "max_retries_down":4, "http_method":"GET", + "http_version": 1.1, "timeout":2, "url_path":"/", "type":"HTTP", @@ -52,11 +54,13 @@ const SingleHealthmonitorBody = ` "admin_state_up":true, "project_id":"83657cfcdfe44cd5920adaf26c48ceea", "delay":5, + "domain_name": "example.com", "name":"db", "expected_codes":"200", "max_retries":2, "max_retries_down":4, "http_method":"GET", + "http_version": 1.1, "timeout":2, "url_path":"/", "type":"HTTP", @@ -73,11 +77,13 @@ const PostUpdateHealthmonitorBody = ` "admin_state_up":true, "project_id":"83657cfcdfe44cd5920adaf26c48ceea", "delay":3, + "domain_name": "www.example.com", "name":"NewHealthmonitorName", "expected_codes":"301", "max_retries":10, "max_retries_down":8, "http_method":"GET", + "http_version": 1.0, "timeout":20, "url_path":"/another_check", "type":"HTTP", @@ -105,6 +111,7 @@ var ( Name: "db", ProjectID: "83657cfcdfe44cd5920adaf26c48ceea", Delay: 5, + DomainName: "example.com", ExpectedCodes: "200", MaxRetries: 2, MaxRetriesDown: 4, @@ -112,6 +119,7 @@ var ( URLPath: "/", Type: "HTTP", HTTPMethod: "GET", + HTTPVersion: 1.1, ID: "5d4b5228-33b0-4e60-b225-9b727c1a20e7", Pools: []monitors.PoolID{{ID: "d459f7d8-c6ee-439d-8713-d3fc08aeed8d"}}, } @@ -120,6 +128,7 @@ var ( Name: "NewHealthmonitorName", ProjectID: "83657cfcdfe44cd5920adaf26c48ceea", Delay: 3, + DomainName: "www.example.com", ExpectedCodes: "301", MaxRetries: 10, MaxRetriesDown: 8, @@ -127,6 +136,7 @@ var ( URLPath: "/another_check", Type: "HTTP", HTTPMethod: "GET", + HTTPVersion: 1.0, ID: "5d4b5228-33b0-4e60-b225-9b727c1a20e7", Pools: []monitors.PoolID{{ID: "d459f7d8-c6ee-439d-8713-d3fc08aeed8d"}}, } @@ -164,10 +174,12 @@ func HandleHealthmonitorCreationSuccessfully(t *testing.T, response string) { "pool_id":"84f1b61f-58c4-45bf-a8a9-2dafb9e5214d", "project_id":"453105b9-1754-413f-aab1-55f1af620750", "delay":20, + "domain_name": "example.com", "name":"db", "timeout":10, "max_retries":5, "max_retries_down":4, + "http_version": 1.1, "url_path":"/check", "expected_codes":"200-299" } @@ -211,9 +223,11 @@ func HandleHealthmonitorUpdateSuccessfully(t *testing.T) { "healthmonitor": { "name": "NewHealthmonitorName", "delay": 3, + "domain_name": "www.example.com", "timeout": 20, "max_retries": 10, "max_retries_down": 8, + "http_version": 1.0, "url_path": "/another_check", "expected_codes": "301" } diff --git a/openstack/loadbalancer/v2/monitors/testing/requests_test.go b/openstack/loadbalancer/v2/monitors/testing/requests_test.go index 5b4bc6732b..eabeb14d5e 100644 --- a/openstack/loadbalancer/v2/monitors/testing/requests_test.go +++ b/openstack/loadbalancer/v2/monitors/testing/requests_test.go @@ -63,9 +63,11 @@ func TestCreateHealthmonitor(t *testing.T) { PoolID: "84f1b61f-58c4-45bf-a8a9-2dafb9e5214d", ProjectID: "453105b9-1754-413f-aab1-55f1af620750", Delay: 20, + DomainName: "example.com", Timeout: 10, MaxRetries: 5, MaxRetriesDown: 4, + HTTPVersion: 1.1, URLPath: "/check", ExpectedCodes: "200-299", }).Extract() @@ -118,9 +120,11 @@ func TestUpdateHealthmonitor(t *testing.T) { actual, err := monitors.Update(client, "5d4b5228-33b0-4e60-b225-9b727c1a20e7", monitors.UpdateOpts{ Name: &name, Delay: 3, + DomainName: "www.example.com", Timeout: 20, MaxRetries: 10, MaxRetriesDown: 8, + HTTPVersion: 1.0, URLPath: "/another_check", ExpectedCodes: "301", }).Extract() From fd16f463a001852b0b3939a4d0303876945e9d07 Mon Sep 17 00:00:00 2001 From: Ludovic Lamarche <ludovic.lamarche@ovhcloud.com> Date: Mon, 27 Dec 2021 12:31:01 +0000 Subject: [PATCH 351/360] add barbican quota implementation --- openstack/keymanager/v1/quotas/doc.go | 32 ++++ openstack/keymanager/v1/quotas/requests.go | 109 +++++++++++++ openstack/keymanager/v1/quotas/results.go | 101 ++++++++++++ openstack/keymanager/v1/quotas/testing/doc.go | 2 + .../keymanager/v1/quotas/testing/fixtures.go | 89 ++++++++++ .../v1/quotas/testing/requests_test.go | 152 ++++++++++++++++++ openstack/keymanager/v1/quotas/urls.go | 34 ++++ 7 files changed, 519 insertions(+) create mode 100644 openstack/keymanager/v1/quotas/doc.go create mode 100644 openstack/keymanager/v1/quotas/requests.go create mode 100644 openstack/keymanager/v1/quotas/results.go create mode 100644 openstack/keymanager/v1/quotas/testing/doc.go create mode 100644 openstack/keymanager/v1/quotas/testing/fixtures.go create mode 100644 openstack/keymanager/v1/quotas/testing/requests_test.go create mode 100644 openstack/keymanager/v1/quotas/urls.go diff --git a/openstack/keymanager/v1/quotas/doc.go b/openstack/keymanager/v1/quotas/doc.go new file mode 100644 index 0000000000..1c9897b927 --- /dev/null +++ b/openstack/keymanager/v1/quotas/doc.go @@ -0,0 +1,32 @@ +/* +Package quotas provides the ability to retrieve and manage Barbican quotas + +Example to Get project quotas + + projectID = "23d5d3f79dfa4f73b72b8b0b0063ec55" + quotasInfo, err := quotas.Get(keyManagerClient, projectID).Extract() + if err != nil { + log.Fatal(err) + } + + fmt.Printf("quotas: %#v\n", quotasInfo) + +Example to Update project quotas + + projectID = "23d5d3f79dfa4f73b72b8b0b0063ec55" + + updateOpts := quotas.UpdateOpts{ + Secrets: gophercloud.IntToPointer(10), + Orders: gophercloud.IntToPointer(20), + Containers: gophercloud.IntToPointer(10), + Consumers: gophercloud.IntToPointer(-1), + Cas: gophercloud.IntToPointer(5), + } + quotasInfo, err := quotas.Update(keyManagerClient, projectID) + if err != nil { + log.Fatal(err) + } + + fmt.Printf("quotas: %#v\n", quotasInfo) +*/ +package quotas diff --git a/openstack/keymanager/v1/quotas/requests.go b/openstack/keymanager/v1/quotas/requests.go new file mode 100644 index 0000000000..819af34a0f --- /dev/null +++ b/openstack/keymanager/v1/quotas/requests.go @@ -0,0 +1,109 @@ +package quotas + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// Get the effective quotas for the project of the requester. The project id of the requester is derived from the authentication token provided in the X-Auth-Token header. +func Get(client *gophercloud.ServiceClient) (r GetResult) { + resp, err := client.Get(getURL(client), &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 { + ToOrderListQuery() (string, error) +} + +// ListOpts provides options to filter the List results. +type ListOpts struct { + // Limit is the amount of containers to retrieve. + Limit int `q:"limit"` + + // Offset is the index within the list to retrieve. + Offset int `q:"offset"` +} + +// ToOrderListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToOrderListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// List retrieves a list of orders. +func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := listURL(client) + if opts != nil { + query, err := opts.ToOrderListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return ProjectQuotaPage{pagination.LinkedPageBase{PageResult: r}} + }) +} + +// GetProjectQuota returns key manager Quotas for a project. +func GetProjectQuota(client *gophercloud.ServiceClient, projectID string) (r GetProjectResult) { + resp, err := client.Get(getProjectURL(client, projectID), &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 { + ToQuotaUpdateMap() (map[string]interface{}, error) +} + +// UpdateOpts represents options used to update the key manager Quotas. +type UpdateOpts struct { + // Secrets represents the number of secrets. A "-1" value means no limit. + Secrets *int `json:"secrets"` + + // Orders represents the number of orders. A "-1" value means no limit. + Orders *int `json:"orders"` + + // Containers represents the number of containers. A "-1" value means no limit. + Containers *int `json:"containers"` + + // Consumers represents the number of consumers. A "-1" value means no limit. + Consumers *int `json:"consumers"` + + // CAS represents the number of cas. A "-1" value means no limit. + CAS *int `json:"cas"` +} + +// ToQuotaUpdateMap builds a request body from UpdateOpts. +func (opts UpdateOpts) ToQuotaUpdateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "project_quotas") +} + +// Update accepts a UpdateOpts struct and updates an existing key manager Quotas using the +// values provided. +func Update(c *gophercloud.ServiceClient, projectID string, opts UpdateOptsBuilder) (r gophercloud.Result) { + b, err := opts.ToQuotaUpdateMap() + if err != nil { + r.Err = err + return + } + resp, err := c.Put(updateProjectURL(c, projectID), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{204}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// Delete a key manager Quotas for a project. +func Delete(c *gophercloud.ServiceClient, projectID string) (r gophercloud.Result) { + resp, err := c.Delete(deleteProjectURL(c, projectID), &gophercloud.RequestOpts{ + OkCodes: []int{204}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} diff --git a/openstack/keymanager/v1/quotas/results.go b/openstack/keymanager/v1/quotas/results.go new file mode 100644 index 0000000000..4eff0802e5 --- /dev/null +++ b/openstack/keymanager/v1/quotas/results.go @@ -0,0 +1,101 @@ +package quotas + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +type commonResult struct { + gophercloud.Result +} + +// Extract is a function that accepts a result and extracts a Quota resource. +func (r commonResult) Extract() (*Quota, error) { + var s struct { + Quota *Quota `json:"quotas"` + } + err := r.ExtractInto(&s) + return s.Quota, err +} + +// GetResult represents the result of a get operation. Call its Extract +// method to interpret it as a Quota. +type GetResult struct { + commonResult +} + +// Quota contains key manager quotas for a project. +type Quota struct { + // Secrets represents the number of secrets. A "-1" value means no limit. + Secrets *int `json:"secrets"` + + // Orders represents the number of orders. A "-1" value means no limit. + Orders *int `json:"orders"` + + // Containers represents the number of containers. A "-1" value means no limit. + Containers *int `json:"containers"` + + // Consumers represents the number of consumers. A "-1" value means no limit. + Consumers *int `json:"consumers"` + + // CAS represents the number of cas. A "-1" value means no limit. + CAS *int `json:"cas"` +} + +type commonProjectResult struct { + gophercloud.Result +} + +// Extract is a function that accepts a result and extracts a Quota resource. +func (r commonProjectResult) Extract() (*Quota, error) { + var s struct { + Quota *Quota `json:"project_quotas"` + } + err := r.ExtractInto(&s) + return s.Quota, err +} + +// GetProjectResult represents the result of a get operation. Call its Extract +// method to interpret it as a Quota. +type GetProjectResult struct { + commonProjectResult +} + +type ProjectQuota struct { + ProjectID string `json:"project_id"` + Quota Quota `json:"project_quotas"` +} + +// ProjectQuotaPage is a single page of quotas results. +type ProjectQuotaPage struct { + pagination.LinkedPageBase +} + +// IsEmpty determines whether or not a page of quotas contains any results. +func (r ProjectQuotaPage) IsEmpty() (bool, error) { + quotas, err := ExtractQuotas(r) + return len(quotas) == 0, err +} + +// NextPageURL extracts the "next" link from the links section of the result. +func (r ProjectQuotaPage) NextPageURL() (string, error) { + var s struct { + Next string `json:"next"` + Previous string `json:"previous"` + } + err := r.ExtractInto(&s) + if err != nil { + return "", err + } + return s.Next, err +} + +// ExtractQuotas returns a slice of ProjectQuota contained in a single page of +// results. +func ExtractQuotas(r pagination.Page) ([]ProjectQuota, error) { + var s struct { + Quotas []ProjectQuota `json:"project_quotas"` + } + err := (r.(ProjectQuotaPage)).ExtractInto(&s) + return s.Quotas, err +} diff --git a/openstack/keymanager/v1/quotas/testing/doc.go b/openstack/keymanager/v1/quotas/testing/doc.go new file mode 100644 index 0000000000..404d517542 --- /dev/null +++ b/openstack/keymanager/v1/quotas/testing/doc.go @@ -0,0 +1,2 @@ +// quotas unit tests +package testing diff --git a/openstack/keymanager/v1/quotas/testing/fixtures.go b/openstack/keymanager/v1/quotas/testing/fixtures.go new file mode 100644 index 0000000000..a3128eb38c --- /dev/null +++ b/openstack/keymanager/v1/quotas/testing/fixtures.go @@ -0,0 +1,89 @@ +package testing + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/keymanager/v1/quotas" +) + +const GetResponseRaw_1 = ` +{ + "quotas": { + "secrets": 10, + "orders": 20, + "containers": -1, + "consumers": 10, + "cas": 5 + } +} +` + +var GetResponse = quotas.Quota{ + Secrets: gophercloud.IntToPointer(10), + Orders: gophercloud.IntToPointer(20), + Containers: gophercloud.IntToPointer(-1), + Consumers: gophercloud.IntToPointer(10), + CAS: gophercloud.IntToPointer(5), +} + +const GetProjectResponseRaw_1 = ` +{ + "project_quotas": { + "secrets": 10, + "orders": 20, + "containers": -1, + "consumers": 10, + "cas": 5 + } +} +` + +const ListResponseRaw_1 = ` +{ + "project_quotas": [ + { + "project_id": "1234", + "project_quotas": { + "secrets": 2000, + "orders": 0, + "containers": -1, + "consumers": null, + "cas": null + } + }, + { + "project_id": "5678", + "project_quotas": { + "secrets": 200, + "orders": 100, + "containers": -1, + "consumers": null, + "cas": null + } + } + ], + "total" : 30 + } +` + +var ExpectedQuotasSlice = []quotas.ProjectQuota{ + { + ProjectID: "1234", + Quota: quotas.Quota{ + Secrets: gophercloud.IntToPointer(2000), + Orders: gophercloud.IntToPointer(0), + Containers: gophercloud.IntToPointer(-1), + Consumers: nil, + CAS: nil, + }, + }, + { + ProjectID: "5678", + Quota: quotas.Quota{ + Secrets: gophercloud.IntToPointer(200), + Orders: gophercloud.IntToPointer(100), + Containers: gophercloud.IntToPointer(-1), + Consumers: nil, + CAS: nil, + }, + }, +} diff --git a/openstack/keymanager/v1/quotas/testing/requests_test.go b/openstack/keymanager/v1/quotas/testing/requests_test.go new file mode 100644 index 0000000000..943f81746a --- /dev/null +++ b/openstack/keymanager/v1/quotas/testing/requests_test.go @@ -0,0 +1,152 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/keymanager/v1/quotas" + fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" + "github.com/gophercloud/gophercloud/pagination" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestGet_1(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/quotas", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprint(w, GetResponseRaw_1) + }) + + q, err := quotas.Get(client.ServiceClient()).Extract() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, q, &GetResponse) +} + +func TestListQuotas(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + th.Mux.HandleFunc("/project-quotas", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprint(w, ListResponseRaw_1) + }) + + count := 0 + err := quotas.List(client.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) { + count++ + + actual, err := quotas.ExtractQuotas(page) + th.AssertNoErr(t, err) + + th.AssertDeepEquals(t, ExpectedQuotasSlice, actual) + + return true, nil + }) + th.AssertNoErr(t, err) + th.AssertEquals(t, count, 1) +} + +func TestListOrdersAllPages(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + th.Mux.HandleFunc("/project-quotas", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprint(w, ListResponseRaw_1) + }) + + allPages, err := quotas.List(client.ServiceClient(), nil).AllPages() + th.AssertNoErr(t, err) + actual, err := quotas.ExtractQuotas(allPages) + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, ExpectedQuotasSlice, actual) +} + +func TestGetProjectQuota_1(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/project-quotas/0a73845280574ad389c292f6a74afa76", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprint(w, GetProjectResponseRaw_1) + }) + + q, err := quotas.GetProjectQuota(client.ServiceClient(), "0a73845280574ad389c292f6a74afa76").Extract() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, q, &GetResponse) +} + +func TestUpdate_1(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/project-quotas/0a73845280574ad389c292f6a74afa76", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PUT") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestJSONRequest(t, r, ` + { + "project_quotas": { + "secrets": 10, + "orders": null, + "containers": 14, + "consumers": 15, + "cas": null + } + }`) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusNoContent) + + }) + + err := quotas.Update(client.ServiceClient(), "0a73845280574ad389c292f6a74afa76", quotas.UpdateOpts{ + Secrets: gophercloud.IntToPointer(10), + Orders: nil, + Containers: gophercloud.IntToPointer(14), + Consumers: gophercloud.IntToPointer(15), + CAS: nil, + }).Err + + th.AssertNoErr(t, err) +} + +func TestDelete_1(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/project-quotas/0a73845280574ad389c292f6a74afa76", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "DELETE") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusNoContent) + + }) + + err := quotas.Delete(client.ServiceClient(), "0a73845280574ad389c292f6a74afa76").Err + + th.AssertNoErr(t, err) +} diff --git a/openstack/keymanager/v1/quotas/urls.go b/openstack/keymanager/v1/quotas/urls.go new file mode 100644 index 0000000000..f823a564ca --- /dev/null +++ b/openstack/keymanager/v1/quotas/urls.go @@ -0,0 +1,34 @@ +package quotas + +import "github.com/gophercloud/gophercloud" + +const resourcePathProject = "project-quotas" +const resourcePath = "quotas" + +func resourceURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL(resourcePath) +} + +func getURL(c *gophercloud.ServiceClient) string { + return resourceURL(c) +} + +func listURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL(resourcePathProject) +} + +func resourceProjectURL(c *gophercloud.ServiceClient, projectID string) string { + return c.ServiceURL(resourcePathProject, projectID) +} + +func getProjectURL(c *gophercloud.ServiceClient, projectID string) string { + return resourceProjectURL(c, projectID) +} + +func updateProjectURL(c *gophercloud.ServiceClient, projectID string) string { + return resourceProjectURL(c, projectID) +} + +func deleteProjectURL(c *gophercloud.ServiceClient, projectID string) string { + return resourceProjectURL(c, projectID) +} From 08ab2cd556e2e5efc519e5481e52373fbdb3d041 Mon Sep 17 00:00:00 2001 From: Nathan Castelein <nathan.castelein@ovh.net> Date: Thu, 13 Jan 2022 10:42:15 +0000 Subject: [PATCH 352/360] endpoint groups feature: add list, get & association --- .../identity/v3/endpointgroups_test.go | 31 +++ .../v3/extensions/endpointgroups/doc.go | 32 +++ .../v3/extensions/endpointgroups/requests.go | 78 ++++++ .../v3/extensions/endpointgroups/results.go | 75 ++++++ .../endpointgroups/testing/requests_test.go | 254 ++++++++++++++++++ .../v3/extensions/endpointgroups/urls.go | 24 ++ 6 files changed, 494 insertions(+) create mode 100644 internal/acceptance/openstack/identity/v3/endpointgroups_test.go create mode 100644 openstack/identity/v3/extensions/endpointgroups/doc.go create mode 100644 openstack/identity/v3/extensions/endpointgroups/requests.go create mode 100644 openstack/identity/v3/extensions/endpointgroups/results.go create mode 100644 openstack/identity/v3/extensions/endpointgroups/testing/requests_test.go create mode 100644 openstack/identity/v3/extensions/endpointgroups/urls.go diff --git a/internal/acceptance/openstack/identity/v3/endpointgroups_test.go b/internal/acceptance/openstack/identity/v3/endpointgroups_test.go new file mode 100644 index 0000000000..ca1fa3e7f7 --- /dev/null +++ b/internal/acceptance/openstack/identity/v3/endpointgroups_test.go @@ -0,0 +1,31 @@ +//go:build acceptance || identity || endpointgroups +// +build acceptance identity endpointgroups + +package v3 + +import ( + "testing" + + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/endpointgroups" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestEndpointGroupsList(t *testing.T) { + clients.SkipRelease(t, "stable/mitaka") + clients.SkipRelease(t, "stable/newton") + clients.SkipRelease(t, "stable/ocata") + clients.SkipRelease(t, "stable/pike") + clients.SkipRelease(t, "stable/queens") + clients.RequireAdmin(t) + + client, err := clients.NewIdentityV3Client() + th.AssertNoErr(t, err) + + allPages, err := endpointgroups.List(client, nil).AllPages() + th.AssertNoErr(t, err) + + allEndpointGroups, err := endpointgroups.ExtractEndpointGroups(allPages) + th.AssertNoErr(t, err) + th.AssertEquals(t, len(allEndpointGroups), 0) +} diff --git a/openstack/identity/v3/extensions/endpointgroups/doc.go b/openstack/identity/v3/extensions/endpointgroups/doc.go new file mode 100644 index 0000000000..6392329ccf --- /dev/null +++ b/openstack/identity/v3/extensions/endpointgroups/doc.go @@ -0,0 +1,32 @@ +/* +Package endpointgroups enables management of OpenStack Identity Endpoint Groups +and Endpoint associations. + +Example to Get an Endpoint Group + + err := endpointgroups.Get(identityClient, endpointGroupID).ExtractErr() + if err != nil { + panic(err) + } + +Example to List all Endpoint Groups by name + + listOpts := endpointgropus.ListOpts{ + Name: "mygroup", + } + + allPages, err := endpointgroups.List(identityClient, listOpts).AllPages() + if err != nil { + panic(err) + } + + allGroups, err := endpointgroups.ExtractEndpointGroups(allPages) + if err != nil { + panic(err) + } + + for _, endpointgroup := range allGroups { + fmt.Printf("%+v\n", endpointgroup) + } +*/ +package endpointgroups diff --git a/openstack/identity/v3/extensions/endpointgroups/requests.go b/openstack/identity/v3/extensions/endpointgroups/requests.go new file mode 100644 index 0000000000..c121226341 --- /dev/null +++ b/openstack/identity/v3/extensions/endpointgroups/requests.go @@ -0,0 +1,78 @@ +package endpointgroups + +import ( + "net/http" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// Get retrieves details on a single endpoint group, by ID. +func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { + _, r.Err = client.Get(resourceURL(client, id), &r.Body, nil) + return +} + +// ListOptsBuilder allows extensions to add additional parameters to +// the List request +type ListOptsBuilder interface { + ToEndpointGroupListQuery() (string, error) +} + +// ListOpts provides options to filter the List results. +type ListOpts struct { + // Name filters the response by endpoint group name. + Name string `q:"name"` +} + +// ToEndpointGroupListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToEndpointGroupListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// List enumerates the endpoint groups. +func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := rootURL(client) + if opts != nil { + query, err := opts.ToEndpointGroupListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return EndpointGroupPage{pagination.LinkedPageBase{PageResult: r}} + }) +} + +// ListForProjects enumerates the endpoint groups associated to a project. +func ListForProjects(client *gophercloud.ServiceClient, projectId string) pagination.Pager { + return pagination.NewPager(client, listEndpointGroupsAssociationURL(client, projectId), func(r pagination.PageResult) pagination.Page { + return EndpointGroupPage{pagination.LinkedPageBase{PageResult: r}} + }) +} + +// CreateProjectAssociation creates an endpoint group to a project association. +func CreateProjectAssociation(client *gophercloud.ServiceClient, id string, projectId string) (r CreateProjectAssociationResult) { + resp, err := client.Put(projectAssociationURL(client, id, projectId), nil, nil, &gophercloud.RequestOpts{ + OkCodes: []int{http.StatusNoContent}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// CheckProjectAssociation checks if an endpoint group is associated to a project. +func CheckProjectAssociation(client *gophercloud.ServiceClient, id string, projectId string) (r CheckProjectAssociationResult) { + resp, err := client.Head(projectAssociationURL(client, id, projectId), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// DeleteProjectAssociation deletes an endpoint group to a project association. +func DeleteProjectAssociation(client *gophercloud.ServiceClient, id string, projectId string) (r DeleteProjectAssociationResult) { + resp, err := client.Delete(projectAssociationURL(client, id, projectId), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} diff --git a/openstack/identity/v3/extensions/endpointgroups/results.go b/openstack/identity/v3/extensions/endpointgroups/results.go new file mode 100644 index 0000000000..0d8d481640 --- /dev/null +++ b/openstack/identity/v3/extensions/endpointgroups/results.go @@ -0,0 +1,75 @@ +package endpointgroups + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +type commonResult struct { + gophercloud.Result +} + +// Extract interprets a GetResult, CreateResult or UpdateResult as a concrete +// EndpointGroup. An error is returned if the original call or the extraction +// failed. +func (r commonResult) Extract() (*EndpointGroup, error) { + var s struct { + EndpointGroup *EndpointGroup `json:"endpoint_group"` + } + err := r.ExtractInto(&s) + return s.EndpointGroup, err +} + +// GetResult is the response from a Get operation. Call its Extract method +// to interpret it as an EndpointGroup. +type GetResult struct { + commonResult +} + +// EndpointGroup represents a group of endpoints matching one or several filter +// criteria. +type EndpointGroup struct { + // ID is the unique ID of the endpoint group. + ID string `json:"id"` + + // Name is the name of the new endpoint group. + Name string `json:"name"` + + // Filters is a map type describing the filter criteria + Filters map[string]interface{} `json:"filters"` + + // Description is the description of the endpoint group + Description string `json:"description"` +} + +// EndpointGroupPage is a single page of EndpointGroup results. +type EndpointGroupPage struct { + pagination.LinkedPageBase +} + +// IsEmpty returns true if no EndpointGroups were returned. +func (r EndpointGroupPage) IsEmpty() (bool, error) { + es, err := ExtractEndpointGroups(r) + return len(es) == 0, err +} + +// ExtractEndpointGroups extracts an EndpointGroup slice from a Page. +func ExtractEndpointGroups(r pagination.Page) ([]EndpointGroup, error) { + var s struct { + EndpointGroups []EndpointGroup `json:"endpoint_groups"` + } + err := (r.(EndpointGroupPage)).ExtractInto(&s) + return s.EndpointGroups, err +} + +type CreateProjectAssociationResult struct { + gophercloud.ErrResult +} + +type CheckProjectAssociationResult struct { + gophercloud.ErrResult +} + +type DeleteProjectAssociationResult struct { + gophercloud.ErrResult +} diff --git a/openstack/identity/v3/extensions/endpointgroups/testing/requests_test.go b/openstack/identity/v3/extensions/endpointgroups/testing/requests_test.go new file mode 100644 index 0000000000..68bdf52c73 --- /dev/null +++ b/openstack/identity/v3/extensions/endpointgroups/testing/requests_test.go @@ -0,0 +1,254 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + "github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/endpointgroups" + "github.com/gophercloud/gophercloud/pagination" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestGetEndpointGroup(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/OS-EP-FILTER/endpoint_groups/24", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + fmt.Fprintf(w, ` + { + "endpoint_group": { + "id": "24", + "filters": { + "interface": "public", + "service_id": "1234", + "region_id": "5678" + }, + "name": "endpointgroup1", + "description": "public endpoint group 1", + "links": { + "self": "https://localhost:5000/v3/OS-EP-FILTER/endpoint_groups/24" + } + } + } + `) + }) + + actual, err := endpointgroups.Get(client.ServiceClient(), "24").Extract() + if err != nil { + t.Fatalf("Failed to extract EndpointGroup: %v", err) + } + + expected := &endpointgroups.EndpointGroup{ + ID: "24", + Filters: map[string]interface{}{ + "interface": "public", + "service_id": "1234", + "region_id": "5678", + }, + Name: "endpointgroup1", + Description: "public endpoint group 1", + } + th.AssertDeepEquals(t, expected, actual) +} + +func TestListEndpointGroups(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/OS-EP-FILTER/endpoint_groups", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + w.Header().Add("Content-Type", "application/json") + fmt.Fprintf(w, ` + { + "endpoint_groups": [ + { + "id": "24", + "filters": { + "interface": "public", + "service_id": "1234", + "region_id": "5678", + }, + "name": "endpointgroup1", + "description": "public endpoint group 1", + "links": { + "self": "https://localhost:5000/v3/OS-EP-FILTER/endpoint_groups/24" + } + }, + { + "id": "25", + "filters": { + "interface": "internal" + }, + "name": "endpointgroup2", + "description": "internal endpoint group 1", + "links": { + "self": "https://localhost:5000/v3/OS-EP-FILTER/endpoint_groups/25" + } + } + ] + } + `) + }) + + endpointgroups.List(client.ServiceClient(), endpointgroups.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + actual, err := endpointgroups.ExtractEndpointGroups(page) + if err != nil { + t.Errorf("Failed to extract EndpointGroups: %v", err) + return false, err + } + + expected := []endpointgroups.EndpointGroup{ + { + ID: "24", + Filters: map[string]interface{}{ + "interface": "public", + "service_id": "1234", + "region_id": "5678", + }, + Name: "endpointgroup1", + Description: "public endpoint group 1", + }, + { + ID: "25", + Filters: map[string]interface{}{ + "interface": "internal", + }, + Name: "endpointgroup2", + Description: "internal endpoint group 1", + }, + } + th.AssertDeepEquals(t, expected, actual) + return true, nil + }) +} + +func TestListEndpointGroupsForProject(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/OS-EP-FILTER/projects/42/endpoint_groups", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + w.Header().Add("Content-Type", "application/json") + fmt.Fprintf(w, ` + { + "endpoint_groups": [ + { + "id": "24", + "filters": { + "interface": "public", + "service_id": "1234", + "region_id": "5678", + }, + "name": "endpointgroup1", + "description": "public endpoint group 1", + "links": { + "self": "https://localhost:5000/v3/OS-EP-FILTER/endpoint_groups/24" + } + }, + { + "id": "25", + "filters": { + "interface": "internal" + }, + "name": "endpointgroup2", + "description": "internal endpoint group 1", + "links": { + "self": "https://localhost:5000/v3/OS-EP-FILTER/endpoint_groups/25" + } + } + ] + } + `) + }) + + endpointgroups.ListForProjects(client.ServiceClient(), "42").EachPage(func(page pagination.Page) (bool, error) { + actual, err := endpointgroups.ExtractEndpointGroups(page) + if err != nil { + t.Errorf("Failed to extract EndpointGroups: %v", err) + return false, err + } + + expected := []endpointgroups.EndpointGroup{ + { + ID: "24", + Filters: map[string]interface{}{ + "interface": "public", + "service_id": "1234", + "region_id": "5678", + }, + Name: "endpointgroup1", + Description: "public endpoint group 1", + }, + { + ID: "25", + Filters: map[string]interface{}{ + "interface": "internal", + }, + Name: "endpointgroup2", + Description: "internal endpoint group 1", + }, + } + th.AssertDeepEquals(t, expected, actual) + return true, nil + }) +} + +func TestCreateProjectAssociation(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/OS-EP-FILTER/endpoint_groups/24/projects/42", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PUT") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + w.WriteHeader(http.StatusNoContent) + }) + + err := endpointgroups.CreateProjectAssociation(client.ServiceClient(), "24", "42").ExtractErr() + if err != nil { + t.Fatalf("Failed to create project association: %v", err) + } +} + +func TestDeleteProjectAssociation(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/OS-EP-FILTER/endpoint_groups/24/projects/42", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "DELETE") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + w.WriteHeader(http.StatusNoContent) + }) + + err := endpointgroups.DeleteProjectAssociation(client.ServiceClient(), "24", "42").ExtractErr() + if err != nil { + t.Fatalf("Failed to create project association: %v", err) + } +} + +func TestCheckProjectAssociation(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/OS-EP-FILTER/endpoint_groups/24/projects/42", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "HEAD") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + w.WriteHeader(http.StatusOK) + }) + + err := endpointgroups.CheckProjectAssociation(client.ServiceClient(), "24", "42").ExtractErr() + if err != nil { + t.Fatalf("Failed to create project association: %v", err) + } +} diff --git a/openstack/identity/v3/extensions/endpointgroups/urls.go b/openstack/identity/v3/extensions/endpointgroups/urls.go new file mode 100644 index 0000000000..5e0f319146 --- /dev/null +++ b/openstack/identity/v3/extensions/endpointgroups/urls.go @@ -0,0 +1,24 @@ +package endpointgroups + +import "github.com/gophercloud/gophercloud" + +const ( + endpointGroupPath = "OS-EP-FILTER/endpoint_groups" + endpointGroupsAssociationPath = "OS-EP-FILTER/projects" +) + +func rootURL(client *gophercloud.ServiceClient) string { + return client.ServiceURL(endpointGroupPath) +} + +func resourceURL(client *gophercloud.ServiceClient, endointGroupID string) string { + return client.ServiceURL(endpointGroupPath, endointGroupID) +} + +func projectAssociationURL(client *gophercloud.ServiceClient, endpointGroupId string, projectId string) string { + return client.ServiceURL(endpointGroupPath, endpointGroupId, "projects", projectId) +} + +func listEndpointGroupsAssociationURL(client *gophercloud.ServiceClient, projectId string) string { + return client.ServiceURL(endpointGroupsAssociationPath, projectId, "endpoint_groups") +} From ba49285edf415c42e4c2d400d04302222d21199e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20M=C3=A9tral-Charvet?= <remi.metral-charvet@corp.ovh.com> Date: Fri, 14 Jan 2022 10:41:10 +0000 Subject: [PATCH 353/360] add network_type and segmentation_id to network creation options --- openstack/networking/v2/networks/requests.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openstack/networking/v2/networks/requests.go b/openstack/networking/v2/networks/requests.go index 00c2eae77d..ef889b6454 100644 --- a/openstack/networking/v2/networks/requests.go +++ b/openstack/networking/v2/networks/requests.go @@ -82,6 +82,8 @@ type CreateOpts struct { TenantID string `json:"tenant_id,omitempty"` ProjectID string `json:"project_id,omitempty"` AvailabilityZoneHints []string `json:"availability_zone_hints,omitempty"` + NetworkType string `json:"provider:network_type,omitempty"` + SegmentationID *int `json:"provider:segmentation_id,omitempty"` } // ToNetworkCreateMap builds a request body from CreateOpts. From cb7bfdec751655975610b14ebbe55b2650b0fa96 Mon Sep 17 00:00:00 2001 From: Julien Tanguy <julien.tanguy@corp.ovh.com> Date: Mon, 27 Jun 2022 14:25:58 +0000 Subject: [PATCH 354/360] Set healthmonitor domain name optional --- .../loadbalancer/v2/monitors/requests.go | 4 +- .../v2/monitors/testing/fixtures_test.go | 94 +++++++++++++++++++ .../v2/monitors/testing/requests_test.go | 41 ++++++++ 3 files changed, 137 insertions(+), 2 deletions(-) diff --git a/openstack/loadbalancer/v2/monitors/requests.go b/openstack/loadbalancer/v2/monitors/requests.go index 676463cd47..ba2d8460f8 100644 --- a/openstack/loadbalancer/v2/monitors/requests.go +++ b/openstack/loadbalancer/v2/monitors/requests.go @@ -107,7 +107,7 @@ type CreateOpts struct { // The domain name, which be injected into the HTTP Host Header to // the backend server for HTTP health check. New in version 2.10. - DomainName string `json:"domain_name"` + DomainName string `json:"domain_name,omitempty"` // Maximum number of seconds for a Monitor to wait for a ping reply // before it times out. The value must be less than the delay value. @@ -205,7 +205,7 @@ type UpdateOpts struct { // The domain name, which be injected into the HTTP Host Header to // the backend server for HTTP health check. New in version 2.10. - DomainName string `json:"domain_name"` + DomainName string `json:"domain_name,omitempty"` // Maximum number of seconds for a Monitor to wait for a ping reply // before it times out. The value must be less than the delay value. diff --git a/openstack/loadbalancer/v2/monitors/testing/fixtures_test.go b/openstack/loadbalancer/v2/monitors/testing/fixtures_test.go index 2516bc5be1..5ed9f11a2c 100644 --- a/openstack/loadbalancer/v2/monitors/testing/fixtures_test.go +++ b/openstack/loadbalancer/v2/monitors/testing/fixtures_test.go @@ -70,6 +70,24 @@ const SingleHealthmonitorBody = ` } ` +// CreatePingHealthmonitorBody is the canned body of a POST request to create a ping healthmonitor. +const CreatePingHealthmonitorBody = ` +{ + "healthmonitor": { + "admin_state_up":true, + "project_id":"83657cfcdfe44cd5920adaf26c48ceea", + "delay":10, + "name":"web", + "max_retries":1, + "max_retries_down":7, + "timeout":1, + "type":"PING", + "pools": [{"id": "84f1b61f-58c4-45bf-a8a9-2dafb9e5214d"}], + "id":"466c8345-28d8-4f84-a246-e04380b0461d" + } +} +` + // PostUpdateHealthmonitorBody is the canned response body of a Update request on an existing healthmonitor. const PostUpdateHealthmonitorBody = ` { @@ -93,6 +111,24 @@ const PostUpdateHealthmonitorBody = ` } ` +// PostUpdatePingHealthmonitorBody is the canned response body of a Update request on an existing healthmonitor. +const PostUpdatePingHealthmonitorBody = ` +{ + "healthmonitor": { + "admin_state_up":true, + "project_id":"83657cfcdfe44cd5920adaf26c48ceea", + "delay":10, + "name":"web", + "max_retries":1, + "max_retries_down":7, + "timeout":1, + "type":"PING", + "pools": [{"id": "84f1b61f-58c4-45bf-a8a9-2dafb9e5214d"}], + "id":"466c8345-28d8-4f84-a246-e04380b0461d" + } +} +` + var ( HealthmonitorWeb = monitors.Monitor{ AdminStateUp: true, @@ -140,6 +176,18 @@ var ( ID: "5d4b5228-33b0-4e60-b225-9b727c1a20e7", Pools: []monitors.PoolID{{ID: "d459f7d8-c6ee-439d-8713-d3fc08aeed8d"}}, } + HealthmonitorWebUpdated = monitors.Monitor{ + AdminStateUp: true, + Name: "web", + ProjectID: "83657cfcdfe44cd5920adaf26c48ceea", + Delay: 10, + MaxRetries: 1, + MaxRetriesDown: 7, + Timeout: 1, + Type: "PING", + ID: "466c8345-28d8-4f84-a246-e04380b0461d", + Pools: []monitors.PoolID{{ID: "84f1b61f-58c4-45bf-a8a9-2dafb9e5214d"}}, + } ) // HandleHealthmonitorListSuccessfully sets up the test server to respond to a healthmonitor List request. @@ -191,6 +239,31 @@ func HandleHealthmonitorCreationSuccessfully(t *testing.T, response string) { }) } +// HandlePingHealthmonitorCreationSuccessfully sets up the test server to respond to a ping healthmonitor creation request +// with a given response. +func HandlePingHealthmonitorCreationSuccessfully(t *testing.T, response string) { + th.Mux.HandleFunc("/v2.0/lbaas/healthmonitors", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + th.TestJSONRequest(t, r, `{ + "healthmonitor": { + "type":"PING", + "pool_id":"84f1b61f-58c4-45bf-a8a9-2dafb9e5214d", + "project_id":"83657cfcdfe44cd5920adaf26c48ceea", + "delay":10, + "name":"web", + "timeout":1, + "max_retries":1, + "max_retries_down":7 + } + }`) + + w.WriteHeader(http.StatusAccepted) + w.Header().Add("Content-Type", "application/json") + fmt.Fprintf(w, response) + }) +} + // HandleHealthmonitorGetSuccessfully sets up the test server to respond to a healthmonitor Get request. func HandleHealthmonitorGetSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v2.0/lbaas/healthmonitors/5d4b5228-33b0-4e60-b225-9b727c1a20e7", func(w http.ResponseWriter, r *http.Request) { @@ -236,3 +309,24 @@ func HandleHealthmonitorUpdateSuccessfully(t *testing.T) { fmt.Fprintf(w, PostUpdateHealthmonitorBody) }) } + +// HandlePingHealthmonitorUpdateSuccessfully sets up the test server to respond to a healthmonitor Update request. +func HandlePingHealthmonitorUpdateSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v2.0/lbaas/healthmonitors/5d4b5228-33b0-4e60-b225-9b727c1a20e7", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PUT") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + th.TestHeader(t, r, "Accept", "application/json") + th.TestHeader(t, r, "Content-Type", "application/json") + th.TestJSONRequest(t, r, `{ + "healthmonitor": { + "delay":3, + "name":"NewHealthmonitorName", + "timeout":20, + "max_retries":10, + "max_retries_down":8 + } + }`) + + fmt.Fprintf(w, PostUpdatePingHealthmonitorBody) + }) +} diff --git a/openstack/loadbalancer/v2/monitors/testing/requests_test.go b/openstack/loadbalancer/v2/monitors/testing/requests_test.go index eabeb14d5e..bfa99de924 100644 --- a/openstack/loadbalancer/v2/monitors/testing/requests_test.go +++ b/openstack/loadbalancer/v2/monitors/testing/requests_test.go @@ -76,6 +76,26 @@ func TestCreateHealthmonitor(t *testing.T) { th.CheckDeepEquals(t, HealthmonitorDb, *actual) } +func TestCreatePingHealthmonitor(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandlePingHealthmonitorCreationSuccessfully(t, CreatePingHealthmonitorBody) + + actual, err := monitors.Create(fake.ServiceClient(), monitors.CreateOpts{ + Type: "PING", + Name: "web", + PoolID: "84f1b61f-58c4-45bf-a8a9-2dafb9e5214d", + ProjectID: "83657cfcdfe44cd5920adaf26c48ceea", + Delay: 10, + Timeout: 1, + MaxRetries: 1, + MaxRetriesDown: 7, + }).Extract() + th.AssertNoErr(t, err) + + th.CheckDeepEquals(t, HealthmonitorWeb, *actual) +} + func TestRequiredCreateOpts(t *testing.T) { res := monitors.Create(fake.ServiceClient(), monitors.CreateOpts{}) if res.Err == nil { @@ -135,6 +155,27 @@ func TestUpdateHealthmonitor(t *testing.T) { th.CheckDeepEquals(t, HealthmonitorUpdated, *actual) } +func TestUpdatePingHealthmonitor(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandlePingHealthmonitorUpdateSuccessfully(t) + + client := fake.ServiceClient() + name := "NewHealthmonitorName" + actual, err := monitors.Update(client, "5d4b5228-33b0-4e60-b225-9b727c1a20e7", monitors.UpdateOpts{ + Name: &name, + Delay: 3, + Timeout: 20, + MaxRetries: 10, + MaxRetriesDown: 8, + }).Extract() + if err != nil { + t.Fatalf("Unexpected Update error: %v", err) + } + + th.CheckDeepEquals(t, HealthmonitorWebUpdated, *actual) +} + func TestDelayMustBeGreaterOrEqualThanTimeout(t *testing.T) { _, err := monitors.Create(fake.ServiceClient(), monitors.CreateOpts{ Type: "HTTP", From e60c9b32c6489942920864c2a290deea434ce486 Mon Sep 17 00:00:00 2001 From: mdelord <38280771+mdelord@users.noreply.github.com> Date: Fri, 7 Oct 2022 16:01:25 +0200 Subject: [PATCH 355/360] Fix BareMetalV1 version (#16) Co-authored-by: Maxime Delord <maxime.delord@ovh.net> --- openstack/client.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/openstack/client.go b/openstack/client.go index 81c907c35b..d051098eb0 100644 --- a/openstack/client.go +++ b/openstack/client.go @@ -363,7 +363,11 @@ func initClientOpts(client *gophercloud.ProviderClient, eo gophercloud.EndpointO // NewBareMetalV1 creates a ServiceClient that may be used with the v1 // bare metal package. func NewBareMetalV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { - return initClientOpts(client, eo, "baremetal") + sc, err := initClientOpts(client, eo, "baremetal") + if !strings.HasSuffix(sc.Endpoint, "v1/") { + sc.ResourceBase = sc.Endpoint + "v1/" + } + return sc, err } // NewBareMetalIntrospectionV1 creates a ServiceClient that may be used with the v1 From 6dd1c8bcc61fd76a0c63a5aa00b5935b163db8ea Mon Sep 17 00:00:00 2001 From: Benjamin Ziehms <benjaminherlin@gmail.com> Date: Mon, 16 Jan 2023 15:22:35 +0000 Subject: [PATCH 356/360] feat(identity): add create, update and delete endpoint group --- .../v3/extensions/endpointgroups/requests.go | 91 +++++++++++++++++++ .../v3/extensions/endpointgroups/results.go | 18 ++++ .../v3/extensions/endpointgroups/urls.go | 24 +++-- 3 files changed, 127 insertions(+), 6 deletions(-) diff --git a/openstack/identity/v3/extensions/endpointgroups/requests.go b/openstack/identity/v3/extensions/endpointgroups/requests.go index c121226341..a188376111 100644 --- a/openstack/identity/v3/extensions/endpointgroups/requests.go +++ b/openstack/identity/v3/extensions/endpointgroups/requests.go @@ -76,3 +76,94 @@ func DeleteProjectAssociation(client *gophercloud.ServiceClient, id string, proj _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } + +// CreateOptsBuilder allows extensions to add additional parameters to +// the Create request. +type CreateOptsBuilder interface { + ToGroupCreateMap() (map[string]interface{}, error) +} + +// CreateOpts provides options used to create a group. +type CreateOpts struct { + // Name is the name of the new endpoint group. + Name string `json:"name" required:"true"` + + // Description is a description of the endpoint group. + Description *string `json:"description,omitempty"` + + // Filters are the filters of the endpoint group. + Filters map[string]interface{} `json:"filters,omitempty"` +} + +// ToGroupCreateMap formats a CreateOpts into a create request. +func (opts CreateOpts) ToGroupCreateMap() (map[string]interface{}, error) { + b, err := gophercloud.BuildRequestBody(opts, "endpoint_group") + if err != nil { + return nil, err + } + + return b, nil +} + +// Create creates a new endpoint group. +func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { + b, err := opts.ToGroupCreateMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Post(createURL(client), &b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{201}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// UpdateOptsBuilder allows extensions to add additional parameters to +// the Update request. +type UpdateOptsBuilder interface { + ToGroupUpdateMap() (map[string]interface{}, error) +} + +// UpdateOpts provides options for updating an endpoint group. +type UpdateOpts struct { + // Name is the name of the endpoint group. + Name string `json:"name,omitempty"` + + // Description is a description of the endpoint group. + Description *string `json:"description,omitempty"` + + // Filters are the filters of the endpoint group. + Filters map[string]interface{} `json:"filters,omitempty"` +} + +// ToGroupUpdateMap formats a UpdateOpts into an update request. +func (opts UpdateOpts) ToGroupUpdateMap() (map[string]interface{}, error) { + b, err := gophercloud.BuildRequestBody(opts, "endpoint_group") + if err != nil { + return nil, err + } + + return b, nil +} + +// Update updates an existing Endpoint Group. +func Update(client *gophercloud.ServiceClient, groupID string, opts UpdateOptsBuilder) (r UpdateResult) { + b, err := opts.ToGroupUpdateMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Patch(updateURL(client, groupID), &b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// Delete deletes an endpoint group. +func Delete(client *gophercloud.ServiceClient, groupID string) (r DeleteResult) { + resp, err := client.Delete(deleteURL(client, groupID), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} diff --git a/openstack/identity/v3/extensions/endpointgroups/results.go b/openstack/identity/v3/extensions/endpointgroups/results.go index 0d8d481640..5585ad4fb7 100644 --- a/openstack/identity/v3/extensions/endpointgroups/results.go +++ b/openstack/identity/v3/extensions/endpointgroups/results.go @@ -73,3 +73,21 @@ type CheckProjectAssociationResult struct { type DeleteProjectAssociationResult struct { gophercloud.ErrResult } + +// CreateResult is the response from a Create operation. Call its Extract method +// to interpret it as an Endpoint Group. +type CreateResult struct { + commonResult +} + +// UpdateResult is the response from an Update operation. Call its Extract +// method to interpret it as an Endpoint Group. +type UpdateResult struct { + commonResult +} + +// DeleteResult is the response from a Delete operation. Call its ExtractErr to +// determine if the request succeeded or failed. +type DeleteResult struct { + gophercloud.ErrResult +} diff --git a/openstack/identity/v3/extensions/endpointgroups/urls.go b/openstack/identity/v3/extensions/endpointgroups/urls.go index 5e0f319146..d3716691b9 100644 --- a/openstack/identity/v3/extensions/endpointgroups/urls.go +++ b/openstack/identity/v3/extensions/endpointgroups/urls.go @@ -11,14 +11,26 @@ func rootURL(client *gophercloud.ServiceClient) string { return client.ServiceURL(endpointGroupPath) } -func resourceURL(client *gophercloud.ServiceClient, endointGroupID string) string { - return client.ServiceURL(endpointGroupPath, endointGroupID) +func resourceURL(client *gophercloud.ServiceClient, endpointGroupID string) string { + return client.ServiceURL(endpointGroupPath, endpointGroupID) } -func projectAssociationURL(client *gophercloud.ServiceClient, endpointGroupId string, projectId string) string { - return client.ServiceURL(endpointGroupPath, endpointGroupId, "projects", projectId) +func projectAssociationURL(client *gophercloud.ServiceClient, endpointGroupID string, projectID string) string { + return client.ServiceURL(endpointGroupPath, endpointGroupID, "projects", projectID) } -func listEndpointGroupsAssociationURL(client *gophercloud.ServiceClient, projectId string) string { - return client.ServiceURL(endpointGroupsAssociationPath, projectId, "endpoint_groups") +func listEndpointGroupsAssociationURL(client *gophercloud.ServiceClient, projectID string) string { + return client.ServiceURL(endpointGroupsAssociationPath, projectID, "endpoint_groups") +} + +func createURL(client *gophercloud.ServiceClient) string { + return client.ServiceURL(endpointGroupPath) +} + +func updateURL(client *gophercloud.ServiceClient, endpointGroupID string) string { + return client.ServiceURL(endpointGroupPath, endpointGroupID) +} + +func deleteURL(client *gophercloud.ServiceClient, endpointGroupID string) string { + return client.ServiceURL(endpointGroupPath, endpointGroupID) } From 260e25a3a6b6c26b444e6b9325cb6099254fbe6c Mon Sep 17 00:00:00 2001 From: Benjamin Ziehms <benjaminherlin@gmail.com> Date: Mon, 16 Jan 2023 15:23:06 +0000 Subject: [PATCH 357/360] feat(identity): add get endpoint by ID --- openstack/identity/v3/endpoints/requests.go | 7 +++++++ openstack/identity/v3/endpoints/results.go | 6 ++++++ 2 files changed, 13 insertions(+) diff --git a/openstack/identity/v3/endpoints/requests.go b/openstack/identity/v3/endpoints/requests.go index 4eba57cd9f..01e9f1858d 100644 --- a/openstack/identity/v3/endpoints/requests.go +++ b/openstack/identity/v3/endpoints/requests.go @@ -88,6 +88,13 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa }) } +// Get retrieves details on a single endpoint, by ID. +func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := client.Get(endpointURL(client, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + // UpdateOptsBuilder allows extensions to add parameters to the Update request. type UpdateOptsBuilder interface { ToEndpointUpdateMap() (map[string]interface{}, error) diff --git a/openstack/identity/v3/endpoints/results.go b/openstack/identity/v3/endpoints/results.go index 886e1b4987..924abada5d 100644 --- a/openstack/identity/v3/endpoints/results.go +++ b/openstack/identity/v3/endpoints/results.go @@ -19,6 +19,12 @@ func (r commonResult) Extract() (*Endpoint, error) { return s.Endpoint, err } +// GetResult is the response from a Get operation. Call its Extract method +// to interpret it as an Endpoint. +type GetResult struct { + commonResult +} + // CreateResult is the response from a Create operation. Call its Extract // method to interpret it as an Endpoint. type CreateResult struct { From 2cf1c8a03e9fd8544796c6b200cd26b96be37442 Mon Sep 17 00:00:00 2001 From: Julien Tanguy <julien.tanguy@corp.ovh.com> Date: Fri, 2 Sep 2022 09:46:24 +0000 Subject: [PATCH 358/360] Add NetworkType and SegmentationID filters when list networks --- openstack/networking/v2/networks/requests.go | 34 +++++++++++--------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/openstack/networking/v2/networks/requests.go b/openstack/networking/v2/networks/requests.go index ef889b6454..a934dadb13 100644 --- a/openstack/networking/v2/networks/requests.go +++ b/openstack/networking/v2/networks/requests.go @@ -19,22 +19,24 @@ type ListOptsBuilder interface { // by a particular network attribute. SortDir sets the direction, and is either // `asc' or `desc'. Marker and Limit are used for pagination. type ListOpts struct { - Status string `q:"status"` - Name string `q:"name"` - Description string `q:"description"` - AdminStateUp *bool `q:"admin_state_up"` - TenantID string `q:"tenant_id"` - ProjectID string `q:"project_id"` - Shared *bool `q:"shared"` - ID string `q:"id"` - Marker string `q:"marker"` - Limit int `q:"limit"` - SortKey string `q:"sort_key"` - SortDir string `q:"sort_dir"` - Tags string `q:"tags"` - TagsAny string `q:"tags-any"` - NotTags string `q:"not-tags"` - NotTagsAny string `q:"not-tags-any"` + Status string `q:"status"` + Name string `q:"name"` + Description string `q:"description"` + AdminStateUp *bool `q:"admin_state_up"` + TenantID string `q:"tenant_id"` + ProjectID string `q:"project_id"` + Shared *bool `q:"shared"` + ID string `q:"id"` + Marker string `q:"marker"` + Limit int `q:"limit"` + SortKey string `q:"sort_key"` + SortDir string `q:"sort_dir"` + Tags string `q:"tags"` + TagsAny string `q:"tags-any"` + NotTags string `q:"not-tags"` + NotTagsAny string `q:"not-tags-any"` + NetworkType string `q:"provider:network_type"` + SegmentationID *int `q:"provider:segmentation_id"` } // ToNetworkListQuery formats a ListOpts into a query string. From a05ca571b999025cd282e61a8ba444b1c64d277e Mon Sep 17 00:00:00 2001 From: Ludovic Lamarche <ludovic.lamarche@ovhcloud.com> Date: Wed, 19 Jul 2023 10:18:27 +0000 Subject: [PATCH 359/360] add loadbalancer additional vips feature --- .../loadbalancer/v2/loadbalancers/requests.go | 4 ++++ .../loadbalancer/v2/loadbalancers/results.go | 10 ++++++++++ .../v2/loadbalancers/testing/fixtures_test.go | 18 +++++++++++++----- .../v2/loadbalancers/testing/requests_test.go | 6 ++++++ 4 files changed, 33 insertions(+), 5 deletions(-) diff --git a/openstack/loadbalancer/v2/loadbalancers/requests.go b/openstack/loadbalancer/v2/loadbalancers/requests.go index 099113c418..94da5ec694 100644 --- a/openstack/loadbalancer/v2/loadbalancers/requests.go +++ b/openstack/loadbalancer/v2/loadbalancers/requests.go @@ -140,6 +140,10 @@ type CreateOpts struct { // Tags is a set of resource tags. Tags []string `json:"tags,omitempty"` + + // The additional ips of the loadbalancer. Subnets must all belong to the same network as the primary VIP. + // New in version 2.26 + AdditionalVIps []AdditionalVip `json:"additional_vips,omitempty"` } // ToLoadBalancerCreateMap builds a request body from CreateOpts. diff --git a/openstack/loadbalancer/v2/loadbalancers/results.go b/openstack/loadbalancer/v2/loadbalancers/results.go index 71f750dd6a..64870d5d39 100644 --- a/openstack/loadbalancer/v2/loadbalancers/results.go +++ b/openstack/loadbalancer/v2/loadbalancers/results.go @@ -77,6 +77,16 @@ type LoadBalancer struct { // Tags is a list of resource tags. Tags are arbitrarily defined strings // attached to the resource. Tags []string `json:"tags"` + + // The additional ips of the loadbalancer. Subnets must all belong to the same network as the primary VIP. + // New in version 2.26 + AdditionalVIps []AdditionalVip `json:"additional_vips"` +} + +// AdditionalVip represent additional ip of a loadbalancer. IpAddress field is optional. +type AdditionalVip struct { + SubnetID string `json:"subnet_id"` + IPAddress string `json:"ip_address,omitempty"` } func (r *LoadBalancer) UnmarshalJSON(b []byte) error { diff --git a/openstack/loadbalancer/v2/loadbalancers/testing/fixtures_test.go b/openstack/loadbalancer/v2/loadbalancers/testing/fixtures_test.go index 5a8ce9e362..06e47c7304 100644 --- a/openstack/loadbalancer/v2/loadbalancers/testing/fixtures_test.go +++ b/openstack/loadbalancer/v2/loadbalancers/testing/fixtures_test.go @@ -19,7 +19,7 @@ import ( const LoadbalancersListBody = ` { "loadbalancers":[ - { + { "id": "c331058c-6a40-4144-948e-b9fb1df9db4b", "project_id": "54030507-44f7-473c-9342-b4d14a95f692", "created_at": "2019-06-30T04:15:37", @@ -52,7 +52,8 @@ const LoadbalancersListBody = ` "admin_state_up": true, "provisioning_status": "PENDING_CREATE", "operating_status": "OFFLINE", - "tags": ["test", "stage"] + "tags": ["test", "stage"], + "additional_vips": [{"subnet_id": "0d4f6a08-60b7-44ab-8903-f7d76ec54095", "ip_address" : "192.168.10.10"}] } ] } @@ -77,7 +78,8 @@ const SingleLoadbalancerBody = ` "admin_state_up": true, "provisioning_status": "PENDING_CREATE", "operating_status": "OFFLINE", - "tags": ["test", "stage"] + "tags": ["test", "stage"], + "additional_vips": [{"subnet_id": "0d4f6a08-60b7-44ab-8903-f7d76ec54095", "ip_address" : "192.168.10.10"}] } } ` @@ -289,7 +291,12 @@ var ( ProvisioningStatus: "PENDING_CREATE", OperatingStatus: "OFFLINE", Tags: []string{"test", "stage"}, - } + AdditionalVIps: []loadbalancers.AdditionalVip{ + { + SubnetID: "0d4f6a08-60b7-44ab-8903-f7d76ec54095", + IPAddress: "192.168.10.10", + }, + }} LoadbalancerUpdated = loadbalancers.LoadBalancer{ ID: "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", ProjectID: "54030507-44f7-473c-9342-b4d14a95f692", @@ -546,7 +553,8 @@ func HandleLoadbalancerCreationSuccessfully(t *testing.T, response string) { "flavor_id": "bba40eb2-ee8c-11e9-81b4-2a2ae2dbcce4", "provider": "haproxy", "admin_state_up": true, - "tags": ["test", "stage"] + "tags": ["test", "stage"], + "additional_vips": [{"subnet_id": "0d4f6a08-60b7-44ab-8903-f7d76ec54095", "ip_address" : "192.168.10.10"}] } }`) diff --git a/openstack/loadbalancer/v2/loadbalancers/testing/requests_test.go b/openstack/loadbalancer/v2/loadbalancers/testing/requests_test.go index 8a420621bb..1d4831c167 100644 --- a/openstack/loadbalancer/v2/loadbalancers/testing/requests_test.go +++ b/openstack/loadbalancer/v2/loadbalancers/testing/requests_test.go @@ -72,6 +72,12 @@ func TestCreateLoadbalancer(t *testing.T) { FlavorID: "bba40eb2-ee8c-11e9-81b4-2a2ae2dbcce4", Provider: "haproxy", Tags: []string{"test", "stage"}, + AdditionalVIps: []loadbalancers.AdditionalVip{ + { + SubnetID: "0d4f6a08-60b7-44ab-8903-f7d76ec54095", + IPAddress: "192.168.10.10", + }, + }, }).Extract() th.AssertNoErr(t, err) From 57fbca2f39645297196bb99884c420b0f8c947e0 Mon Sep 17 00:00:00 2001 From: Romain Dupont <romain.dupont-2@ovhcloud.com> Date: Wed, 13 Mar 2024 15:05:05 +0000 Subject: [PATCH 360/360] add manila quotasets --- .../sharedfilesystems/v2/quotasets_test.go | 101 +++++++++++ .../sharedfilesystems/v2/quotasets/doc.go | 70 ++++++++ .../v2/quotasets/requests.go | 121 +++++++++++++ .../sharedfilesystems/v2/quotasets/results.go | 167 ++++++++++++++++++ .../v2/quotasets/testing/fixtures.go | 111 ++++++++++++ .../v2/quotasets/testing/requests_test.go | 109 ++++++++++++ .../sharedfilesystems/v2/quotasets/urls.go | 34 ++++ 7 files changed, 713 insertions(+) create mode 100644 internal/acceptance/openstack/sharedfilesystems/v2/quotasets_test.go create mode 100644 openstack/sharedfilesystems/v2/quotasets/doc.go create mode 100644 openstack/sharedfilesystems/v2/quotasets/requests.go create mode 100644 openstack/sharedfilesystems/v2/quotasets/results.go create mode 100644 openstack/sharedfilesystems/v2/quotasets/testing/fixtures.go create mode 100644 openstack/sharedfilesystems/v2/quotasets/testing/requests_test.go create mode 100644 openstack/sharedfilesystems/v2/quotasets/urls.go diff --git a/internal/acceptance/openstack/sharedfilesystems/v2/quotasets_test.go b/internal/acceptance/openstack/sharedfilesystems/v2/quotasets_test.go new file mode 100644 index 0000000000..1ce359cc56 --- /dev/null +++ b/internal/acceptance/openstack/sharedfilesystems/v2/quotasets_test.go @@ -0,0 +1,101 @@ +//go:build acceptance +// +build acceptance + +package v2 + +import ( + "testing" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/quotasets" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestGet(t *testing.T) { + client, err := clients.NewSharedFilesystemV2Client() + th.AssertNoErr(t, err) + + // Get the quotaset for the current tenant + quotaset, err := quotasets.Get(client, client.TenantID).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, quotaset) +} + +func TestUpdate(t *testing.T) { + client, err := clients.NewSharedFilesystemV2Client() + th.AssertNoErr(t, err) + + // Get the quotaset for the current tenant + quotaset, err := quotasets.Get(client, client.TenantID).Extract() + th.AssertNoErr(t, err) + + // Update the quotaset + updateOpts := quotasets.UpdateOpts{ + Gigabytes: gophercloud.Int(100), + } + quotaset, err = quotasets.Update(client, client.TenantID, updateOpts).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, quotaset) +} + +func TestGetByShareType(t *testing.T) { + client, err := clients.NewSharedFilesystemV2Client() + th.AssertNoErr(t, err) + + // Get the quotaset for the current tenant + quotaset, err := quotasets.GetByShareType(client, client.TenantID, "default").Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, quotaset) +} + +func TestUpdateByShareType(t *testing.T) { + client, err := clients.NewSharedFilesystemV2Client() + th.AssertNoErr(t, err) + + // Get the quotaset for the current tenant + quotaset, err := quotasets.GetByShareType(client, client.TenantID, "default").Extract() + th.AssertNoErr(t, err) + + // Update the quotaset + updateOpts := quotasets.UpdateOpts{ + Gigabytes: gophercloud.Int(100), + } + quotaset, err = quotasets.UpdateByShareType(client, client.TenantID, "default", updateOpts).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, quotaset) +} + +func TestGetByUser(t *testing.T) { + client, err := clients.NewSharedFilesystemV2Client() + th.AssertNoErr(t, err) + + // Get the quotaset for the current tenant + quotaset, err := quotasets.GetByUser(client, client.TenantID, "admin").Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, quotaset) +} + +func TestUpdateByUser(t *testing.T) { + client, err := clients.NewSharedFilesystemV2Client() + th.AssertNoErr(t, err) + + // Get the quotaset for the current tenant + quotaset, err := quotasets.GetByUser(client, client.TenantID, "admin").Extract() + th.AssertNoErr(t, err) + + // Update the quotaset + updateOpts := quotasets.UpdateOpts{ + Gigabytes: gophercloud.Int(100), + } + quotaset, err = quotasets.UpdateByUser(client, client.TenantID, "admin", updateOpts).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, quotaset) +} diff --git a/openstack/sharedfilesystems/v2/quotasets/doc.go b/openstack/sharedfilesystems/v2/quotasets/doc.go new file mode 100644 index 0000000000..68ed1c0495 --- /dev/null +++ b/openstack/sharedfilesystems/v2/quotasets/doc.go @@ -0,0 +1,70 @@ +/* +Package quotasets provides information and interaction with the quotasets API for the OpenStack Shared Filesystems service. + +Example to Get a Quota Set + + quotaset, err := quotasets.Get(sharedfilesystemsClient, "tenant-id").Extract() + if err != nil { + panic(err) + } + + fmt.Printf("%+v\n", quotaset) + +Example to Update a Quota Set + + updateOpts := quotasets.UpdateOpts{ + Gigabytes: gophercloud.IntToPointer(100), + } + + quotaset, err := quotasets.Update(sharedfilesystemsClient, "tenant-id", updateOpts).Extract() + if err != nil { + panic(err) + } + + fmt.Printf("%+v\n", quotaset) + +Example to Get a Quota Set by Share Type + + quotaset, err := quotasets.GetByShareType(sharedfilesystemsClient, "tenant-id", "default").Extract() + if err != nil { + panic(err) + } + + fmt.Printf("%+v\n", quotaset) + +Example to Update a Quota Set by Share Type + + updateOpts := quotasets.UpdateOpts{ + Gigabytes: gophercloud.IntToPointer(100), + } + + quotaset, err := quotasets.UpdateByShareType(sharedfilesystemsClient, "tenant-id", "default", updateOpts).Extract() + if err != nil { + panic(err) + } + + fmt.Printf("%+v\n", quotaset) + +Example to Get a Quota Set by User + + quotaset, err := quotasets.GetByUser(sharedfilesystemsClient, "tenant-id", "user-id").Extract() + if err != nil { + panic(err) + } + + fmt.Printf("%+v\n", quotaset) + +Example to Update a Quota Set by User + + updateOpts := quotasets.UpdateOpts{ + Gigabytes: gophercloud.IntToPointer(100), + } + + quotaset, err := quotasets.UpdateByUser(sharedfilesystemsClient, "tenant-id", "user-id", updateOpts).Extract() + if err != nil { + panic(err) + } + + fmt.Printf("%+v\n", quotaset) +*/ +package quotasets diff --git a/openstack/sharedfilesystems/v2/quotasets/requests.go b/openstack/sharedfilesystems/v2/quotasets/requests.go new file mode 100644 index 0000000000..4e417c4cc0 --- /dev/null +++ b/openstack/sharedfilesystems/v2/quotasets/requests.go @@ -0,0 +1,121 @@ +package quotasets + +import ( + "github.com/gophercloud/gophercloud" +) + +// Get returns data about a previously created QuotaSet. +func Get(client *gophercloud.ServiceClient, tenantID string) (r GetResult) { + resp, err := client.Get(getURL(client, tenantID), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// GetDetail returns detailed Networking Quotas for a project. +func GetDetail(client *gophercloud.ServiceClient, projectID string) (r GetDetailResult) { + resp, err := client.Get(getDetailURL(client, projectID), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// Updates the quotas for the given tenantID. +func Update(client *gophercloud.ServiceClient, tenantID string, opts UpdateOptsBuilder) (r UpdateResult) { + reqBody, err := opts.ToManillaQuotaUpdateMap() + if err != nil { + r.Err = err + return + } + + resp, err := client.Put(updateURL(client, tenantID), reqBody, &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// Get returns data about a previously created QuotaSet for a share type. +func GetByShareType(client *gophercloud.ServiceClient, tenantID string, share_type string) (r GetResult) { + resp, err := client.Get(getURLbyShareType(client, tenantID, share_type), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// Updates the quotas for a given sharetype. +func UpdateByShareType(client *gophercloud.ServiceClient, tenantID string, share_type string, opts UpdateOptsBuilder) (r UpdateResult) { + reqBody, err := opts.ToManillaQuotaUpdateMap() + if err != nil { + r.Err = err + return + } + + resp, err := client.Put(updateURLByShareType(client, tenantID, share_type), reqBody, &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// Get returns data about a previously created QuotaSet for a user id. +func GetByUser(client *gophercloud.ServiceClient, tenantID string, user_id string) (r GetResult) { + resp, err := client.Get(getURLbyUser(client, tenantID, user_id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// Updates the quotas for a given user. +func UpdateByUser(client *gophercloud.ServiceClient, tenantID string, user_id string, opts UpdateOptsBuilder) (r UpdateResult) { + reqBody, err := opts.ToManillaQuotaUpdateMap() + if err != nil { + r.Err = err + return + } + + resp, err := client.Put(updateURLByUser(client, tenantID, user_id), reqBody, &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// Options for Updating the quotas of a Tenant. +// All int-values are pointers so they can be nil if they are not needed. +// You can use gopercloud.IntToPointer() for convenience +type UpdateOpts struct { + // Gigabytes is the total size of share storage for the project in gigabytes. + Gigabytes *int `json:"gigabytes,omitempty"` + + // Snapshots is the total number of share snapshots for the project. + Snapshots *int `json:"snapshots,omitempty"` + + // Shares is the total number of shares for the project. + Shares *int `json:"shares,omitempty"` + + // SnapshotGigabytes is the total size of share snapshots for the project in gigabytes. + SnapshotGigabytes *int `json:"snapshot_gigabytes,omitempty"` + + // Share network is the total number of share networks for the project. + ShareNetworks *int `json:"share_networks,omitempty"` + + // Share groups is the total number of share groups for the project. + ShareGroups *int `json:"share_groups,omitempty"` + + // Share group snapshots is the total number of share group snapshots for the project. + ShareGroupSnapshots *int `json:"share_group_snapshots,omitempty"` + + // Share Replicas is the total number of share replicas for the project. + ShareReplicas *int `json:"share_replicas,omitempty"` + + // Share Replica Gigabytes is the total size of share replicas for the project in gigabytes. + ShareReplicaGigabytes *int `json:"share_replica_gigabytes,omitempty"` + + // PerShareGigabytes is the maximum size of a share for the project in gigabytes. + PerShareGigabytes *int `json:"per_share_gigabytes,omitempty"` +} + +// UpdateOptsBuilder enables extensins to add parameters to the update request. +type UpdateOptsBuilder interface { + // Extra specific name to prevent collisions with interfaces for other quotas + // (e.g. neutron) + ToManillaQuotaUpdateMap() (map[string]interface{}, error) +} + +// ToComputeManillaUpdateMap builds the update options into a serializable +// format. +func (opts UpdateOpts) ToManillaQuotaUpdateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "quota_set") + +} diff --git a/openstack/sharedfilesystems/v2/quotasets/results.go b/openstack/sharedfilesystems/v2/quotasets/results.go new file mode 100644 index 0000000000..6017bec1d7 --- /dev/null +++ b/openstack/sharedfilesystems/v2/quotasets/results.go @@ -0,0 +1,167 @@ +package quotasets + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// QuotaSet is a set of operational limits that allow for control of manila +// usage. +type QuotaSet struct { + // Gigabytes is the total size of share storage for the project in gigabytes. + Gigabytes *int `json:"gigabytes,omitempty"` + + // Snapshots is the total number of share snapshots for the project. + Snapshots *int `json:"snapshots,omitempty"` + + // Shares is the total number of shares for the project. + Shares *int `json:"shares,omitempty"` + + // SnapshotGigabytes is the total size of share snapshots for the project in gigabytes. + SnapshotGigabytes *int `json:"snapshot_gigabytes,omitempty"` + + // Share network is the total number of share networks for the project. + ShareNetworks *int `json:"share_networks,omitempty"` + + // Share groups is the total number of share groups for the project. + ShareGroups *int `json:"share_groups,omitempty"` + + // Share group snapshots is the total number of share group snapshots for the project. + ShareGroupSnapshots *int `json:"share_group_snapshots,omitempty"` + + // Share Replicas is the total number of share replicas for the project. + ShareReplicas *int `json:"share_replicas,omitempty"` + + // Share Replica Gigabytes is the total size of share replicas for the project in gigabytes. + ShareReplicaGigabytes *int `json:"share_replica_gigabytes,omitempty"` + + // PerShareGigabytes is the maximum size of a share for the project in gigabytes. + PerShareGigabytes *int `json:"per_share_gigabytes,omitempty"` + + // Backups is the maximum number of backups allowed for each project. + Backups *int `json:"backups,omitempty"` + + // BackupsGigabytes is the maximum number of gigabytes for the backups allowed for each project. + BackupsGigabytes *int `json:"backup_gigabytes,omitempty"` +} + +// QuotaDetailSet represents details of both operational limits of shares file system resources for a project +// and the current usage of those resources. +type QuotaDetailSet struct { + // Gigabytes is the total size of share storage for the project in gigabytes. + Gigabytes QuotaDetail `json:"gigabytes,omitempty"` + + // Snapshots is the total number of share snapshots for the project. + Snapshots QuotaDetail `json:"snapshots,omitempty"` + + // Shares is the total number of shares for the project. + Shares QuotaDetail `json:"shares,omitempty"` + + // SnapshotGigabytes is the total size of share snapshots for the project in gigabytes. + SnapshotGigabytes QuotaDetail `json:"snapshot_gigabytes,omitempty"` + + // Share network is the total number of share networks for the project. + ShareNetworks QuotaDetail `json:"share_networks,omitempty"` + + // Share groups is the total number of share groups for the project. + ShareGroups QuotaDetail `json:"share_groups,omitempty"` + + // Share group snapshots is the total number of share group snapshots for the project. + ShareGroupSnapshots QuotaDetail `json:"share_group_snapshots,omitempty"` + + // Share Replicas is the total number of share replicas for the project. + ShareReplicas QuotaDetail `json:"share_replicas,omitempty"` + + // Share Replica Gigabytes is the total size of share replicas for the project in gigabytes. + ShareReplicaGigabytes QuotaDetail `json:"share_replica_gigabytes,omitempty"` + + // PerShareGigabytes is the maximum size of a share for the project in gigabytes. + PerShareGigabytes QuotaDetail `json:"per_share_gigabytes,omitempty"` + + // Backups is the maximum number of backups allowed for each project. + Backups QuotaDetail `json:"backups,omitempty"` + + // BackupsGigabytes is the maximum number of gigabytes for the backups allowed for each project. + BackupsGigabytes QuotaDetail `json:"backup_gigabytes,omitempty"` +} + +// QuotaDetail is a set of details about a single operational limit that allows +// for control of shared file system usage. +type QuotaDetail struct { + // InUse is the current number of provisioned/allocated resources of the + // given type. + InUse int `json:"in_use"` + + // Reserved is a transitional state when a claim against quota has been made + // but the resource is not yet fully online. + Reserved int `json:"reserved"` + + // Limit is the maximum number of a given resource that can be + // allocated/provisioned. This is what "quota" usually refers to. + Limit int `json:"limit"` +} + +// QuotaSetPage stores a single page of all QuotaSet results from a List call. +type QuotaSetPage struct { + pagination.SinglePageBase +} + +// IsEmpty determines whether or not a QuotaSetsetPage is empty. +func (page QuotaSetPage) IsEmpty() (bool, error) { + ks, err := ExtractQuotaSets(page) + return len(ks) == 0, err +} + +// ExtractQuotaSets interprets a page of results as a slice of QuotaSets. +func ExtractQuotaSets(r pagination.Page) ([]QuotaSet, error) { + var s struct { + QuotaSets []QuotaSet `json:"quotas"` + } + err := (r.(QuotaSetPage)).ExtractInto(&s) + return s.QuotaSets, err +} + +type quotaResult struct { + gophercloud.Result +} + +type quotaDetailResult struct { + gophercloud.Result +} + +// Extract is a function that accepts a result and extracts a QuotaDetailSet resource. +func (r quotaDetailResult) Extract() (*QuotaDetailSet, error) { + var s struct { + Quota *QuotaDetailSet `json:"quota_set"` + } + err := r.ExtractInto(&s) + return s.Quota, err +} + +// Extract is a method that attempts to interpret any QuotaSet resource response +// as a QuotaSet struct. +func (r quotaResult) Extract() (*QuotaSet, error) { + var s struct { + QuotaSet *QuotaSet `json:"quota_set"` + } + err := r.ExtractInto(&s) + return s.QuotaSet, err +} + +// GetResult is the response from a Get operation. Call its Extract method to +// interpret it as a QuotaSet. +type GetResult struct { + quotaResult +} + +// UpdateResult is the response from a Update operation. Call its Extract method +// to interpret it as a QuotaSet. +type UpdateResult struct { + quotaResult +} + +// GetDetailResult represents the detailed result of a get operation. Call its Extract +// method to interpret it as a Quota. +type GetDetailResult struct { + quotaDetailResult +} diff --git a/openstack/sharedfilesystems/v2/quotasets/testing/fixtures.go b/openstack/sharedfilesystems/v2/quotasets/testing/fixtures.go new file mode 100644 index 0000000000..9159c4e933 --- /dev/null +++ b/openstack/sharedfilesystems/v2/quotasets/testing/fixtures.go @@ -0,0 +1,111 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + th "github.com/gophercloud/gophercloud/testhelper" + client "github.com/gophercloud/gophercloud/testhelper/client" +) + +var ( + ShareType = "default" + tenantID = "7b8b4e4e93774e07ab9a05867129fd17" + userID = "d8a9bde6cb724fad9e6ec83cbc7f43e9" +) + +const ExpectedInitialQuotaSet = ` +{ + quota_set: { + "giga_bytes": 10, + "snapshots": 10, + "shares": 10, + "snapchot_gigabytes": 10, + "share_networks": 10, + "share_groups": 10, + "share_group_snapshots": 10, + "share_replicas": 10, + "share_replica_gigabytes": 10, + "per_share_gigabytes": 10, + } +} +` + +const ExpectedUpdatedQuotaSet = ` +{ + quota_set: { + "giga_bytes": 100, + "snapshots": 100, + "shares": 100, + "snapchot_gigabytes": 100, + "share_networks": 100, + "share_groups": 100, + "share_group_snapshots": 100, + "share_replicas": 100, + "share_replica_gigabytes": 100, + "per_share_gigabytes": 100, + } +} +` + +func HandleGetQuotaSetSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/quota-sets/"+tenantID, func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, ExpectedInitialQuotaSet) + }) +} + +func HandleUpdateQuotaSetSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/quota-sets/"+tenantID, func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PUT") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, ExpectedUpdatedQuotaSet) + }) +} + +func HandleGetByShareTypeSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/quota-sets/"+tenantID+"?share_type="+ShareType, func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, ExpectedInitialQuotaSet) + }) +} + +func HandleUpdateByShareTypeSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/quota-sets/"+tenantID+"?share_type="+ShareType, func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PUT") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, ExpectedUpdatedQuotaSet) + }) +} + +func HandleGetByUserSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/quota-sets/"+tenantID+"?user_id="+userID, func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, ExpectedInitialQuotaSet) + }) +} + +func HandleUpdateByUserSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/quota-sets/"+tenantID+"?user_id="+userID, func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PUT") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, ExpectedUpdatedQuotaSet) + }) +} diff --git a/openstack/sharedfilesystems/v2/quotasets/testing/requests_test.go b/openstack/sharedfilesystems/v2/quotasets/testing/requests_test.go new file mode 100644 index 0000000000..e5face049c --- /dev/null +++ b/openstack/sharedfilesystems/v2/quotasets/testing/requests_test.go @@ -0,0 +1,109 @@ +package testing + +import ( + "testing" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/quotasets" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestGetQuotaSet(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleGetQuotaSetSuccessfully(t) + + actual, err := quotasets.Get(client.ServiceClient(), tenantID).Extract() + th.AssertNoErr(t, err) + + th.CheckDeepEquals(t, ExpectedInitialQuotaSet, actual) +} + +func TestUpdateQuotaSet(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleUpdateQuotaSetSuccessfully(t) + + actual, err := quotasets.Update(client.ServiceClient(), tenantID, quotasets.UpdateOpts{ + Gigabytes: gophercloud.IntToPointer(100), + Snapshots: gophercloud.IntToPointer(100), + Shares: gophercloud.IntToPointer(100), + SnapshotGigabytes: gophercloud.IntToPointer(100), + ShareNetworks: gophercloud.IntToPointer(100), + ShareGroups: gophercloud.IntToPointer(100), + ShareGroupSnapshots: gophercloud.IntToPointer(100), + ShareReplicas: gophercloud.IntToPointer(100), + ShareReplicaGigabytes: gophercloud.IntToPointer(100), + PerShareGigabytes: gophercloud.IntToPointer(100), + }).Extract() + + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, ExpectedUpdatedQuotaSet, actual) +} + +func TestGetByShareType(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleGetByShareTypeSuccessfully(t) + + actual, err := quotasets.GetByShareType(client.ServiceClient(), tenantID, ShareType).Extract() + th.AssertNoErr(t, err) + + th.CheckDeepEquals(t, ExpectedInitialQuotaSet, actual) +} + +func TestUpdateByShareType(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleUpdateByShareTypeSuccessfully(t) + + actual, err := quotasets.UpdateByShareType(client.ServiceClient(), tenantID, ShareType, quotasets.UpdateOpts{ + Gigabytes: gophercloud.IntToPointer(100), + Snapshots: gophercloud.IntToPointer(100), + Shares: gophercloud.IntToPointer(100), + SnapshotGigabytes: gophercloud.IntToPointer(100), + ShareNetworks: gophercloud.IntToPointer(100), + ShareGroups: gophercloud.IntToPointer(100), + ShareGroupSnapshots: gophercloud.IntToPointer(100), + ShareReplicas: gophercloud.IntToPointer(100), + ShareReplicaGigabytes: gophercloud.IntToPointer(100), + PerShareGigabytes: gophercloud.IntToPointer(100), + }).Extract() + th.AssertNoErr(t, err) + + th.CheckDeepEquals(t, ExpectedUpdatedQuotaSet, actual) +} + +func TestGetByUser(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleGetByUserSuccessfully(t) + + actual, err := quotasets.GetByUser(client.ServiceClient(), tenantID, userID).Extract() + th.AssertNoErr(t, err) + + th.CheckDeepEquals(t, ExpectedInitialQuotaSet, actual) +} + +func TestUpdateByUser(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleUpdateByUserSuccessfully(t) + + actual, err := quotasets.UpdateByUser(client.ServiceClient(), tenantID, userID, quotasets.UpdateOpts{ + Gigabytes: gophercloud.IntToPointer(100), + Snapshots: gophercloud.IntToPointer(100), + Shares: gophercloud.IntToPointer(100), + SnapshotGigabytes: gophercloud.IntToPointer(100), + ShareNetworks: gophercloud.IntToPointer(100), + ShareGroups: gophercloud.IntToPointer(100), + ShareGroupSnapshots: gophercloud.IntToPointer(100), + ShareReplicas: gophercloud.IntToPointer(100), + ShareReplicaGigabytes: gophercloud.IntToPointer(100), + PerShareGigabytes: gophercloud.IntToPointer(100), + }).Extract() + th.AssertNoErr(t, err) + + th.CheckDeepEquals(t, ExpectedUpdatedQuotaSet, actual) +} diff --git a/openstack/sharedfilesystems/v2/quotasets/urls.go b/openstack/sharedfilesystems/v2/quotasets/urls.go new file mode 100644 index 0000000000..0be704ab15 --- /dev/null +++ b/openstack/sharedfilesystems/v2/quotasets/urls.go @@ -0,0 +1,34 @@ +package quotasets + +import "github.com/gophercloud/gophercloud" + +const resourcePath = "quota-sets" +const resourcePathDetail = "detail" + +func getURL(c *gophercloud.ServiceClient, tenantID string) string { + return c.ServiceURL(resourcePath, tenantID) +} + +func getDetailURL(c *gophercloud.ServiceClient, tenantID string) string { + return c.ServiceURL(resourcePath, tenantID, resourcePathDetail) +} + +func updateURL(c *gophercloud.ServiceClient, tenantID string) string { + return c.ServiceURL(resourcePath, tenantID) +} + +func getURLbyShareType(c *gophercloud.ServiceClient, tenantID string, share_type string) string { + return c.ServiceURL(resourcePath, tenantID) + "?share_type=" + share_type +} + +func updateURLByShareType(c *gophercloud.ServiceClient, tenantID string, share_type string) string { + return c.ServiceURL(resourcePath, tenantID) + "?share_type=" + share_type +} + +func getURLbyUser(c *gophercloud.ServiceClient, tenantID string, user_id string) string { + return c.ServiceURL(resourcePath, tenantID) + "?user_id=" + user_id +} + +func updateURLByUser(c *gophercloud.ServiceClient, tenantID string, user_id string) string { + return c.ServiceURL(resourcePath, tenantID) + "?user_id=" + user_id +}